DEV Community

Nashe Omirro
Nashe Omirro

Posted on • Edited on

Having fun with svelte stores using store proxies

So imagine this one big global store with lots of nested properties, how would you write a way to derive a piece of state from that store? One answer would be to just react to that store inside svelte itself, and maybe a utility function could handle the deriving logic:

import { store } from './store.js';
import { deriveFoo } from './store.utils.js';

$: foo = deriveFoo($store);
Enter fullscreen mode Exit fullscreen mode

another more refined answer is to use a derived store to then export it inside of svelte:

import { derivedFoo } from './store.js';

<p>{$derivedFoo}</p>
Enter fullscreen mode Exit fullscreen mode

In most cases, those two are good enough and is the standard way to get value out, but there is a more radical approach to derivation, and it has big upside to it as well.

abusing the store contract

Before getting into the third approach, we'll just understand what the store contract is in svelte. In a nutshell, it is the automatic process of svelte subscribing and setting a store inside components:

import { store } from './store.js';

function updateStore() {
  // assignments becomes a call to the store's set property
  $store.foo = $store.foo + 1;
}

// svelte spots this and auto-subscribes for you
<p>{$store.foo}</p>
Enter fullscreen mode Exit fullscreen mode

What makes them truly awesome though is that they don't care what kind of object it's actually accessing, so long as it has a subscribe property and optional set property and both conform to the store contract (subscribe gets passed a callback and set gets passed a value).

Now knowing this, we are able to create our own derived store without the derived function from svelte!

const store = writable({ foo: 0 });

const derivedFoo = {
  subscribe: (cb) => store.subscribe((state) => cb(state.foo),
};
Enter fullscreen mode Exit fullscreen mode

In the above example, we are basically hijacking the subscription process and letting us extract foo and pass it to the given callback. This means that users of the store are actually subscribed to the big gigantic store but only get a small part of it.

derived and writable?

Okay, it's nice to understand what's actually happening but why do this when you can just use derived?

The answer to that is the fun part, unlike derived, we can supply a set property! Making the store writable!

const store = writable({ foo: 0 });

const foo = {
  subscribe: (cb) => store.subscribe((state) => cb(state.foo),
  set: (value) => store.set((state) => { 
    ...state,
    foo: value
  }
};
Enter fullscreen mode Exit fullscreen mode

And to use it:

import { foo } from './store.js';

function updateFoo() {
  // we can correctly set foo!
  $foo = $foo + 1
}

// we can use it just like derived
<p>{$foo}</p>
Enter fullscreen mode Exit fullscreen mode

And that's not all, another feature that uses the store contract when dealing with stores is the bind: directive, which means we could do this!

import { foo } from './store.js';

<input bind:value={$foo} />; 
Enter fullscreen mode Exit fullscreen mode

Binding to a derived state! Pretty cool, yeah?

Conclusion

You don't have to stop there, this is just a simple example, the truth is that as long as you follow the store contract, anything goes and you can get crazier with it.

but I guess stores will probably kick the bucket soon because of the new runes coming to town, understanding this though will still help you try to do the same with runes! Or maybe not... apart from the "you can do this?!" factor, realistically there isn't really a common use case this solves that the other, more simpler and readable approaches don't solve. It is still kinda neat~

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.