IndexedDB

Posted by neverset on April 1, 2021

IndexedDB is an inbuilt non-relational database for browser to persistently store data in the browser and to perform various types of queries on it. Dexie.js is a minimalistic wrapper for IndexedDB to handle offline data storage in our web applications.

  • non-relational database
  • support usage of web applications even in offline mode
  • application can only access data from an IndexedDB database that runs on the same domain or subdomain as itself

    installation

    npm i -s dexie@next dexie-react-hooks

usage

create IndexedDB

const initializeDb = indexedDB.open('name_of_database', version)

create object

initializeDb.onupgradeneeded = () => {
    const database = initializeDb.result
    database.createObjectStore('name_of_object_store', {autoIncrement: true})
}

with Dexie the process of creating IndexedDB and objects are simplified:

const db = new Dexie('exampleDatabase')
db.version(1).stores({
    name_of_object_store: '++id, name, price',
    name_of_another_object_store: '++id, title'
})

query

const items = await db.friends
    .where('price').above(10)
    .toArray();

example

the template is here: https://github.com/ebenezerdon/market-list-template

#./src/App.js
import Dexie from 'dexie'
import { useLiveQuery } from "dexie-react-hooks"
const db = new Dexie('MarketList');
db.version(1).stores(
{ items: "++id,name,price,itemHasBeenPurchased" }
)

const App = () => {
    //useLiveQuery hook is to watch for changes and re-render our React component when an update is made to the IndexedDB database
    const allItems = useLiveQuery(() => db.items.toArray(), []);
    if (!allItems) return null

    //add item
    const addItemToDb = async event => {
        event.preventDefault()
        const name = document.querySelector('.item-name').value
        const price = document.querySelector('.item-price').value
        await db.items.add({
        name,
        price: Number(price),
        itemHasBeenPurchased: false
        })
    }
    //delete item
    const removeItemFromDb = async id => {
        await db.items.delete(id)
        }
        //mark item as purchased
        const markAsPurchased = async (id, event) => {
        if (event.target.checked) {
            await db.items.update(id, {itemHasBeenPurchased: true})
        }
        else {
            await db.items.update(id, {itemHasBeenPurchased: false})
        }
    }

}

const itemData = allItems.map(({ id, name, price, itemHasBeenPurchased }) => (
    <div className="row" key={id}>
        <p className="col s5">
        <label>
            <input
            type="checkbox"
            checked={itemHasBeenPurchased}
            onChange={event => markAsPurchased(id, event)}
            />
            <span className="black-text">{name}</span>
        </label>
        </p>
        <p className="col s5">${price}</p>
        <i onClick={() => removeItemFromDb(id)} className="col s2 material-icons delete-button">
        delete
        </i>
    </div>
))
return (
    <div className="container">
        <h3 className="green-text center-align">Market List App</h3>
        <form className="add-item-form" onSubmit={event => addItemToDb(event)} >
        <input type="text" className="item-name" placeholder="Name of item" required/>
        <input type="number" step=".01" className="item-price" placeholder="Price in USD" required/>
        <button type="submit" className="waves-effect waves-light btn right">Add item</button>
        </form>
        {allItems.length > 0 &&
        <div className="card white darken-1">
            <div className="card-content">
            <form action="#">
                { itemData }
            </form>
            </div>
        </div>
        }
    </div>
)