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
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"
});
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);
}
})
}
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>
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>
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()
}
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)
Thanks for putting this together, helpful. I think there are a couple of mistakes:
progress
should be name of table i.e.user
{'key': name, 'value': inputValue }
From what I can see that's not the issue. Should instead be
Note:
Table.put
requires aJSObject
notJSON
And the function should probably have arguments
And should probably be
async
, added arrow functions syntax for fun and profit