Search code examples
sveltedexie

Is there a better way to write this code in svelte using dexie?


Trying to learn svelte and using dexie. Is there a better way to get data to the array from indexedDB?

My functions.js file

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

db.version(1).stores({
  todo: "++id,note",
});

App.svelte

<script>
  import { onMount } from "svelte";
  import { db } from "./functions";

  let newnote = "";
  let addnote = (e) => {
    db.todo.add({ note: newnote });
    db.todo.toArray().then(items => {
      todos = items;
    });
  };

  $:todos = [];
  let start = () => {
    db.todo.toArray().then(items => {
      todos = items;
    });
  };

  onMount(() => {
    start();
  });
</script>

<main>
  <h1 id="title">ToDo</h1>
  <input type="text" id="newnote" bind:value={newnote} />
  <input type="button" value="Add" on:click={addnote} />

  <br>
  {#each todos as todo}
    {todo.note} <br>
  {/each}
</main>

Btw, can you keep this code hidden in the app? If so, how? Or is that not necessary?


Solution

  • You'd get the simplest integration by using the new liveQuery() from dexie.

    npm install dexie@next
    

    Using dexie@next is because version 4.x has better Svelte- and SvelteKit support

    Play around with this in the following codesandbox: https://codesandbox.io/s/svelte-with-dexie-livequery-2n8bd?file=/App.svelte

    To translate your specific example:

    <script>
      import { liveQuery } from "dexie";
      import { db } from "./functions";
    
      let newnote = "";
      let addnote = (e) => {
        db.todo.add({ note: newnote });
      };
    
      const todos = liveQuery(
        () => db.todo.toArray()
      );
    </script>
    
    <main>
      <h1 id="title">ToDo</h1>
      <input type="text" id="newnote" bind:value={newnote} />
      <input type="button" value="Add" on:click={addnote} />
    
      <br>
      {#if $todos}
        {#each $todos as todo}
          {todo.note} <br>
        {/each}
      {/if}
    </main>
    
    

    Some explanations:

    • Dexie's liveQuery() returns an observable compatible with rxjs, es-observable proposal as well as Svelte's store contract)
    • The initial result will be undefined (that's why we need to #if todos) as the results are asynchronous.
    • When you mutate the DB you don't need to care about re-querying - this happens automatically.
    • Even if the DB is mutated from another tab or window, your view will also update.
    • Your query can be how simple or complex as it requires. It can even be an async function that awaits several queries in a row and returns the final result. Everything it queries will be observed and the entire function will be re-executed whenever database is mutated in a way that would affect any of the queries involved. Observation is fine grained to optimize performance. For example if you query all todoItems with certain tags (db.todo.where({tags: 'sports'})) assuming tags is a multiEntry intexed array, no update will occur unless a todoItem with that tag is updated, added or deleted, or if another todoItem gets the "sports" tag.

    I've blogged about this feature and how it can enhance ReactJS apps, however, I wasn't aware of the Svelte store contract until recently and happily understood that we have Svelte integration for free.