DEV Community

Nico Heinrich
Nico Heinrich

Posted on • Edited on

Svelte Stores x Dexie 2.0

My first post was about connecting a svelte store to the indexedDB. This one will be an update on the same topic. Keep reading if you want a completely different approach that is more practial in most cases!

What I want to achieve

I want to save data in the indexedDB and use that data in my app.

The problem

As the idexedDB works asynchronously, most of the time the page will be loaded before the data, which results in an ugly flicker when the elements are updated due to svelte's reactive nature. So, the data should be loaded from a store rather than the indexedDB itself whenever possible. Obviously, it has to be loaded once on the initial page load — but with an SPA that is it!

The solution

A svelte store with a special function that gets the data from the indexedDB. That is basically it but there are some important subtleties that I will explain in a minute.

Install Dexie

In order to use this, you need to install Dexie.js:

npm install dexie
Enter fullscreen mode Exit fullscreen mode

Create db.js

This is the very minimalistic set-up for your indexedDB that sits in your lib folder:

import Dexie from 'dexie';

export const db = new Dexie("user");

db.version(1).stores({
  user: "key, value"
});
Enter fullscreen mode Exit fullscreen mode

Create stores.js

This is the actual store that you can use in your +page.svelte. As you can see, it has a function called sync which gets the data from the indexedDB with Dexie and sets the data of userData. It also returns a promise which will be very handy in a second.

import { writable } from "svelte/store";
import { db } from "$lib/db";

export const userData = writable([]);

userData.sync = function() {
    return new Promise (async (resolve, reject) => {
        try {
            const data = await db.user.toArray();
            userData.set(data);

            resolve();
        } catch (error) {
            console.error(error);

            reject (error);
        }
    })
}
Enter fullscreen mode Exit fullscreen mode

How To Load

import { userData } from "@stores"; // I am using an alias here

<script>
    function handleMount() {
        userData.sync()
    }

    onMount(handleMount);
</script>

<main>
    {#each $userData as data}
        <p>{data.name}, {data.age}</p>
    {/each}
</main>
Enter fullscreen mode Exit fullscreen mode

Do Stuff When The Data Has Been Loaded

Sometimes you need to wait for the data until you can call a function. This is no longer a problem, as you can just use .then after the sync-Function.

import { userData } from "@stores"; // I am using an alias here

<script>
    function handleMount() {
        userData.sync()
        .then(() => {
            // do stuff!
        })
    }

    onMount(handleMount);
</script>

<main>
    {#each $userData as data}
        <p>{data.name}, {data.age}</p>
    {/each}
</main>
Enter fullscreen mode Exit fullscreen mode

How To Save

To save data in the indexedDB, simply use the Dexie API and then update the store:

    function saveData() {
        db.user.put({ name, inputValue });
        userData.sync()
    }
Enter fullscreen mode Exit fullscreen mode

I really like this work flow, as it gives me way more control than the last solution (and Dexie's liveQuery) but is still really, really simple!

Cheers,

Nico

Top comments (2)

Collapse
 
owennewo profile image
Owen Williams • Edited

Thanks for putting this together, helpful. I think there are a couple of mistakes:

  1. progress should be name of table i.e. user
const data = await db.user.toArray();
Enter fullscreen mode Exit fullscreen mode
  1. The object you 'put' is not valid json it should be something like {'key': name, 'value': inputValue }
db.user.put({'key': name, 'value': inputValue });
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dskvr profile image
Sandwich • Edited

The object you 'put' is not valid json it should be something like {'key': name, 'value': inputValue }

From what I can see that's not the issue. Should instead be

{ name, age } 
//same as 
{ name: name, age: age }
Enter fullscreen mode Exit fullscreen mode

Note: Table.put requires a JSObject not JSON

And the function should probably have arguments

    function putUserData(name, age) {
        db.user.put({ name, age });
        userData.sync()
    }
Enter fullscreen mode Exit fullscreen mode

And should probably be async, added arrow functions syntax for fun and profit

    const putUserData = async (name, age) => {
        await db.user.put({ name, age });
        await userData.sync()
    }
Enter fullscreen mode Exit fullscreen mode