Dexie livequery with Svelte 5 runes

81 Views Asked by At

I am currently using Dexie 4.x and Svelte 4.x and taking advantage of livequery to update state when data in the underlying DB changes. Looking ahead to Svelte 5, I'm wondering how to integrate livequery with runes ($state, $derived). I know that Svelte stores will still be supported in Svelete 5, but I'm interested in leveraging the fine-grained reactivity benefits of runes and the ability to use runes outside components.

Svelte 5 uses signals for reactivity which (according to my early understanding) means that when there is a change, the entire nested state doesn't get invalidated and only the parts of the dom dependent on the specific state are updated.

let friends= $state([]);

class Friend{
    best = $state();
    name= $state();
    
    constructor(name) {
        this.best = false,
        this.best= text
    }

    toggleBest() {
        this.best= !this.best;
    }
}

friends.push(new Friend("Peter"));
friends.push(new Friend("Mary"));

Given the above example, toggling the best property of one of the friends does not invalidate the entire array. The fine-grained reactivity using signals allows Svelte to skip all the checks to see what's been updated and can simply update only the dependent parts of the dom.

Since liveQuery relies on stores and basically replaces the friends array with a new array (I think), we lose the benefits of fine-grained reactivity.

I may be missing something here since I don't have a complete understanding. It's also possible that using liveQuery AND taking advantage of signals is not really possible since liveQuery just returns the results of the query each time Dexie detects a change.

I found the following pull request useful in trying to understand how state works in Svelte 5 especially since they started using proxies.

https://github.com/sveltejs/svelte/pull/9739

1

There are 1 best solutions below

3
David Fahlander On

As I understand runes, they enable a way to declare reactive logic outside of svelte components and within JS/TS files instead. This allows refactoring of reactive code from components into library functions. Dexie's liveQuery() returns an Ecmascript observable compatible with both RxJS and Svelte stores. And Svelte consumes them elegantly by automatically subscribing and unsubscribing them under the hood without ever missing to do so. This will still be supported in Svelte 5 so I see no reason to change that for now.

However, the Svelte examples on dexie.org could probably be updated to the Svelte 5 recommendations of declaring variables that are derived from props or state. The following component from dexie.org svelte docs:

<!-- FriendList.svelte --> 
<script>
  import { liveQuery } from "dexie";
  import { db } from "./db";

  // Query parameters:
  export let minAge = 18;
  export let maxAge = 65;

  //
  // Query
  //
  $: friends = liveQuery(async () => {
    //
    // Query Dexie's API
    //
    const friends = await db.friends
      .where('age')
      .between(minAge, maxAge)
      .toArray();

    // Return result
    return friends;
  });
</script>
<ul>
  {#if $friends}
    {#each $friends as friend (friend.id)}
      <li>{friend.name}, {friend.age}</li>
    {/each}
  {/if}
</ul>

...could probably be written like this in Svelte 5:

<!-- FriendList.svelte --> 
<script>
  import { liveQuery } from "dexie";
  import { db } from "./db";

  // Query parameters:
  let {
    minAge = 18,
    maxAge = 65
  } = $props();

  //
  // Query
  //
  let friends = $derived(liveQuery(async () => {
    //
    // Query Dexie's API
    //
    const friends = await db.friends
      .where('age')
      .between(minAge, maxAge)
      .toArray();

    // Return result
    return friends;
  }));
</script>
<ul>
  {#if $friends}
    {#each $friends as friend (friend.id)}
      <li>{friend.name}, {friend.age}</li>
    {/each}
  {/if}
</ul>

This opens up for refactoring the query into a JS/TS file, which is good. Still, the return value (friends in this case) will be an Observable / Store so a library function would need something like RxJS if it would need to manipulate the result before returning it further to the caller, since the $friends - magic can only happen in the svelte files. It would have been nice if there were a built-in rune in Svelte5 that could "unpack" a store to its value - which would eliminate the need of RxJS for this but I couldn't find anything like that in the docs.

Any feedback or comments are welcome.