# react-indexed-db-hook

react-indexed-db-hook is forked from [react-indexed-db](https://github.com/assuncaocharles/react-indexed-db). This library is a wrapper around the browser's IndexedDB database in an "easier to use" React Hook.

## Installation

```js
npm install react-indexed-db-hook
```

## Creating the DB

You can choose to work with the indexed db as an context or to use it as a hook. However, I don't plan on supporting the context, but it's there if you want it.

### To use it as a hook

- First initialized your DB before to be able to use the hooks inside other components:

```js
//DBConfig.js|tsx

export const DBConfig = {
  name: 'MyDB',
  version: 1,
  objectStoresMeta: [
    {
      store: 'people',
      storeConfig: { keyPath: 'id', autoIncrement: true },
      storeSchema: [
        { name: 'name', keypath: 'name', options: { unique: false } },
        { name: 'email', keypath: 'email', options: { unique: false } }
      ]
    }
  ]
};
```

```js
//App.js|tsx

import React from 'react';
import { DBConfig } from './DBConfig';
import { initDB } from 'react-indexed-db-hook';

initDB(DBConfig);

const App: React.FC = () => {
  return <div>...</div>;
};
```

### To use it as a context:

- First you have to declare inside `<IndexedDB></IndexedDB>` all the components you want to be able access the DB:

```js
import { IndexedDB } from 'react-indexed-db-hook';
import PanelExample from './Panel';

function App() {
  return (
    <IndexedDB
      name="MyDB"
      version={1}
      objectStoresMeta={[
        {
          store: 'people',
          storeConfig: { keyPath: 'id', autoIncrement: true },
          storeSchema: [
            { name: 'name', keypath: 'name', options: { unique: false } },
            { name: 'email', keypath: 'email', options: { unique: false } }
          ]
        }
      ]}>
      <Panel />
    </IndexedDB>
  );
}
```

## Accessing and working with the DB

- In any component inside this context or in an app using the hooks after creating the DB you can consume it like bellow:

```js
// Context
import { AccessDB } from 'react-indexed-db-hook';

export default function PanelExample() {
  return (
    <AccessDB objectStore="people">
      {db => {
        console.log('MyDB: ', db);
        return <div>{JSON.stringify(db)}</div>;
      }}
    </AccessDB>
  );
}

// Hooks
import { useIndexedDB } from 'react-indexed-db-hook';

export default function PanelExample() {
  const db = useIndexedDB('people');

  return (<div>{JSON.stringify(db)}</div>);
}
```

#### getByID(id)

It returns the object that is stored in the objectStore by its id.
**getByID** returns a promise that is resolved when we have the object or rejected if an error occurred.

Usage example:

```js
// Context
import { AccessDB } from 'react-indexed-db-hook';

<AccessDB objectStore="people">
  {({ getById }) => {
    const [person, setPerson] = useState(null);
    getById('people', 1).then(
      personFromDB => {
        setPerson(personFromDB);
      },
      error => {
        console.log(error);
      }
    );
    return <div>{person}</div>;
  }}
</AccessDB>;

// Hooks
import { useIndexedDB } from 'react-indexed-db-hook';

function ByID() {
  const { getByID } = useIndexedDB('people');
  const [person, setPerson] = useState();

  useEffect(() => {
    getById(1).then(personFromDB => {
      setPerson(personFromDB);
    });
  }, []);

  return <div>{person}</div>;
}
```

#### getAll()

It returns an array of all the items in the given objectStore.
**getAll** returns a promise that is resolved when we have the array of items or rejected if an error occurred.

Usage example:

```js
// Context
import { AccessDB } from 'react-indexed-db-hook';

<AccessDB objectStore="people">
  {({ getAll }) => {
    const [persons, setPersons] = useState(null);
    getAll().then(
      peopleFromDB => {
        setPersons(peopleFromDB);
      },
      error => {
        console.log(error);
      }
    );
    return (
      <div>
        {personsFromDB.map(person => (
          <span>{person}</span>
        ))}
      </div>
    );
  }}
</AccessDB>;

// Hooks
import { useIndexedDB } from 'react-indexed-db-hook';

function ShowAll() {
  const { getAll } = useIndexedDB('people');
  const [persons, setPersons] = useState();

  useEffect(() => {
    getAll().then(personsFromDB => {
      setPersons(personsFromDB);
    });
  }, []);

  return (
    <div>
      {personsFromDB.map(person => (
        <span>{person}</span>
      ))}
    </div>
  );
}
```

#### getByIndex(indexName, key)

It returns an stored item using an objectStore's index.
The first parameter is the index and the second is the item to query.
**getByIndex** returns a promise that is resolved when the item successfully returned or rejected if an error occurred.

Usage example:

```js
// Context
import { AccessDB } from 'react-indexed-db-hook';

<AccessDB objectStore="people">
  {({ getByIndex }) => {
    const [person, setPerson] = useState(null);
    getByIndex('name', 'Dave').then(
      personFromDB => {
        setPerson(peopleFromDB);
      },
      error => {
        console.log(error);
      }
    );
    return <div>{person}</div>;
  }}
</AccessDB>;

// Hooks
import { useIndexedDB } from 'react-indexed-db-hook';

function ByIndex() {
  const { getByIndex } = useIndexedDB('people');
  const [person, setPerson] = useState();

  useEffect(() => {
    getByIndex('name', 'Dave').then(personFromDB => {
      setPerson(peopleFromDB);
    });
  }, []);
  return <div>{person}</div>;
}
```

#### add(value, key)

It Adds to the given objectStore the key and value pair.
The firt parameter is the value and the second is the key (if not auto-generated).
**add** returns a promise that is resolved when the value was added or rejected if an error occurred.

Usage example (add without a key since it's configured to be auto generated):

```js
// Context
import { AccessDB } from 'react-indexed-db-hook';

<AccessDB objectStore="people">
  {({ add }) => {
    const handleClick = () => {
      add({ name: 'name', email: 'email' }).then(
        event => {
          console.log('ID Generated: ', event.target.result);
        },
        error => {
          console.log(error);
        }
      );
    };

    return <button onClick={handleClick}>Add</button>;
  }}
</AccessDB>;

// Hooks
import { useIndexedDB } from 'react-indexed-db-hook';

function AddMore() {
  const { add } = useIndexedDB('people');
  const [person, setPerson] = useState();

  const handleClick = () => {
    add({ name: 'name', email: 'email' }).then(
      event => {
        console.log('ID Generated: ', event.target.result);
      },
      error => {
        console.log(error);
      }
    );
  };

  return <button onClick={handleClick}>Add</button>;
}
```

#### update(value, key?)

It updates the given value in the objectStore.
The first parameter is the value to update and the second is the key (if there is no key don't provide it).
**update** returns a promise that is resolved when the value was updated or rejected if an error occurred.

Usage example (update without a key):

```js
// Context
import { AccessDB } from 'react-indexed-db-hook';

<AccessDB objectStore="people">
  {({ update }) => {
    return (
      <button
        onClick={() => {
          update({ id: 3, name: 'NewName', email: 'NewEmail' }).then(
            () => {
              // Do something after update
            },
            error => {
              console.log(error);
            }
          );
        }}>
        Update
      </button>
    );
  }}
</AccessDB>;

// Hooks
import { useIndexedDB } from 'react-indexed-db-hook';

function Edit() {
  const { update } = useIndexedDB('people');
  const [person, setPerson] = useState();

  const handleClick = () => {
    update({ id: 3, name: 'NewName', email: 'NewNEmail' }).then(event => {
      alert('Edited!');
    });
  };

  return <button onClick={handleClick}>Update</button>;
}
```

#### delete(key)

deletes the object that correspond with the key from the objectStore.
The first parameter is the key to delete.
**delete** returns a promise that is resolved when the value was deleted or rejected if an error occurred.

Usage example:

```js
// Context
import { AccessDB } from 'react-indexed-db-hook';

<AccessDB objectStore="people">
  {({ deleteRecord }) => {
    const handleClick = () => {
      deleteRecord(3).then(event => {
        alert('Deleted!');
      });
    };
    return <button onClick={handleClick}>Delete</button>;
  }}
</AccessDB>;

// Hooks
import { useIndexedDB } from 'react-indexed-db-hook';

function Delete() {
  const { deleteRecord } = useIndexedDB('people');

  const handleClick = () => {
    deleteRecord(3).then(event => {
      alert('Deleted!');
    });
  };

  return <button onClick={handleClick}>Delete</button>;
}
```

#### openCursor(cursorCallback, keyRange)

It opens an objectStore cursor to enable iterating on the objectStore.
The first parameter is a callback function to run when the cursor succeeds to be opened and the second is optional IDBKeyRange object.
**openCursor** returns a promise that is resolved when the cursor finishes running or rejected if an error occurred.

Usage example:

```js
// Context
import { AccessDB } from 'react-indexed-db-hook';

<AccessDB objectStore="people">
  {({ openCursor }) => {
    const handleClick = () => {
      openCursor(evt => {
        var cursor = evt.target.result;
        if (cursor) {
          console.log(cursor.value);
          cursor.continue();
        } else {
          console.log('Entries all displayed.');
        }
      }, IDBKeyRange.bound('A', 'F'));
    };
    return <button onClick={handleClick}>Run cursor</button>;
  }}
</AccessDB>;

// Hooks
import { useIndexedDB } from 'react-indexed-db-hook';

function Open() {
  const { openCursor } = useIndexedDB('people');

  const handleClick = () => {
    openCursor(evt => {
      var cursor = evt.target.result;
      if (cursor) {
        console.log(cursor.value);
        cursor.continue();
      } else {
        console.log('Entries all displayed.');
      }
    }, IDBKeyRange.bound('A', 'F'));
  };

  return <button onClick={handleClick}>Run cursor</button>;
}
```

#### clear()

It clears all the data in the objectStore.
**clear** returns a promise that is resolved when the objectStore was cleared or rejected if an error occurred.

Usage example:

```js
// Context
import { AccessDB } from 'react-indexed-db-hook';

<AccessDB>
  {({ clear }) => {
    const handleClick = () => {
      clear().then(() => {
        alert('All Clear!');
      });
    };
    return <button onClick={handleClick}>Clear All</button>;
  }}
</AccessDB>;

// Hooks
import { useIndexedDB } from 'react-indexed-db-hook';

function ClearAll() {
  const { clear } = useIndexedDB('people');

  const handleClick = () => {
    clear().then(() => {
      alert('All Clear!');
    });
  };

  return <button onClick={handleClick}>Clear All</button>;
}
```

## TODO

- [ ] Improve this documentation
- [x] Implement Hooks `const {getAll, add ...} = useIndexedDB({name, version, dbSchema?})`
- [ ] Handle `getAll()` perfomance issue regarding re-render
- [ ] Implement examples/Demos

## License

Released under the terms of the [MIT License](LICENSE).
