Skip to content

Instantly share code, notes, and snippets.

@dimfeld
Last active June 25, 2020 15:54
Show Gist options
  • Save dimfeld/0ff834a2fcd6f7bf575eeb21471c290e to your computer and use it in GitHub Desktop.
Save dimfeld/0ff834a2fcd6f7bf575eeb21471c290e to your computer and use it in GitHub Desktop.
Immer Store
import { writable, Readable } from 'svelte/store';
import { produce, enableAllPlugins } from 'immer';
enableAllPlugins();
export interface ImmerStore<T> {
update: (updateFn: (draft: T) => T | void) => T;
subscribe: Readable<T>['subscribe'];
}
export default function immerStore<T = any>(initialValue: T): ImmerStore<T> {
let store = writable<T>(initialValue);
let update = (updateFn: (draft: T) => T | void) => {
let ret;
store.update((value) => {
ret = produce(value, updateFn) as T;
return ret;
});
return ret;
};
return {
update,
subscribe: store.subscribe,
};
}
@opensas
Copy link

opensas commented Jun 19, 2020

This seems really interesting, could you provide a working example showing the differences with a regular writable store?

@dimfeld
Copy link
Author

dimfeld commented Jun 23, 2020

@opensas Here's a really contrived example, but should make it clear how it makes things easier when working with highly nested data:
https://svelte.dev/repl/a03de6f7c44b4b68b6e6c90598a3c72f?version=3.23.2

@opensas
Copy link

opensas commented Jun 23, 2020

That's great, if I understand correctly you could do something pretty similar with the writable store, like

		w.update((obj) => {
			obj.a.b.c.d.push(newValue)
			return obj
		});

but in that case you would be mutating the original object.

Would that also work with the autosubscription method? I tried this:

		$w.a.b.c.d.push(newValue)
		$w = $w

and it works, but

		$i.a.b.c.d.push(newValue)
		$i = $i

silently fails

@dimfeld
Copy link
Author

dimfeld commented Jun 24, 2020

The idea behind Immer is to keep the objects immutable while retaining the convenience of mutable-style code. If you're ok with mutating then the version with the writable is fine, though I believe you would have to return { ...obj } to make sure that update sees that it actually changed.

As for this:

$i.a.b.c.d.push(newValue)
$i = $i

The immer store as written in the Gist doesn't expose a set method. You could add one. Generally the first line would not work though since once Immer starts using an object it throws an error if you mutate the object outside immer's produce function. That may only happen in development mode though.

@opensas
Copy link

opensas commented Jun 25, 2020

The idea behind Immer is to keep the objects immutable while retaining the convenience of mutable-style code.

Yes, that's exactly what I have in mind. I wish it could be almost transparent to work with stores in an immutable way, that's why I asked about auto-subscription support. Also, getting rid of the $i = $i would be nice, it still brings visual noise to my eyes.

I think that if it gets polished, an immutable store would be a great addition to svelte's arsenal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment