0.1.3 • Published 2 years ago

@feltcoop/svelte-mutable-store v0.1.3

Weekly downloads
-
License
Unlicense
Repository
github
Last release
2 years ago

@feltcoop/svelte-mutable-store

Svelte stores for mutable values with the immutable compiler option

npm: @feltcoop/svelte-mutable-store

full example: feltcoop.github.io/svelte-mutable-store

minimal example: repl

todo

  • finalize API for 1.0 (help wanted!)
  • write automated tests (the website demo covers all behavior, but automated tests are good & make good docs)
  • write the API readme section
  • video intro?

motivation

The Svelte compiler has an immutable option that's disabled by default. When detecting value changes with immutable disabled, Svelte assumes all objects and functions are not equal because they could have been mutated. By enabling immutable, the developer is telling the compiler, "I won't mutate things, so knowing that, please avoid as much wasted work as you can", and it then detects value changes using simple referential equality.

for more on immutable, see this short writeup in the prototype that became this library

This library currently offers no guidance on whether you should enable immutable. There are complex ergonomic and performance tradeoffs that depend on your personal preferences, code style, architecture, usecases, and the specifics of your runtime data.

For developers who choose to enable immutable, this library offers a mutable store that allows mutation without breaking reactivity. Sometimes mutation is required, like with the unclonable WeakMap and APIs that mutate things outside of your control, and other times it's desirable, like with large collections that are expensive to copy.

for a demo of why this new store is needed, see the full and minimal REPL examples

You may be wondering: why not use <svelte:options immutable={true|false} /> to opt in or out of immutability at the component level?

  • it adds mental overhead to ensure the option and usage stay in sync in each component
  • it's error-prone because there's no compile-time help for detecting mistakes
  • it makes developers context-switch as they move around a codebase because components can behave in two different ways
  • the lack of granularity is less efficient because it applies to the whole component, not specific values

Some caveats:

  • when reading values in components, the actual value is $store.value not just $store
  • you must pass store around as props and component-level vars, not the inner .value, so Svelte can detect changes
  • mutable swaps between two stable object references, which may cause issues in some cases, while safeMutable creates a new object reference on each change, which is safer but slightly less efficient

usage

npm i -D @feltcoop/svelte-mutable-store

Enable immutable either globally or per-component; otherwise mutable values work fine in writable stores. In other words, you should not use this library unless you're also enabling immutable because of the ergonomic and performance downsides.

This library's API is intentionally different from writable so it stands out, highlighting its incompatible semantics. Maybe that's not best?

view this example in a REPL on svelte.dev

<svelte:options immutable />

<script>
	import {mutable, safeMutable} from '@feltcoop/svelte-mutable-store';
	import {writable} from 'svelte/store';

	const someObj = {};
	const data = [[someObj, 1]];
	// `mutable` is the more efficient option; swaps between 2 stable objects refs
	const a = mutable(new WeakMap(data));
	// `safeMutable` is slightly less efficient; creates a new object ref every mutation
	const b = safeMutable(new WeakMap(data));
	// `writable` doesn't work in this case with `immutable` enabled
	const c = writable(new WeakMap(data));

	const increment = () => {
		a.mutate(($a) => {
			$a.set(someObj, $a.get(someObj) + 1);
		});
		b.mutate(($b) => {
			$b.set(someObj, $b.get(someObj) + 1);
		});
		$c.set(someObj, $c.get(someObj) + 1);
		$c = $c;
	};
	const reset = () => {
		a.swap(new WeakMap(data));
		b.swap(new WeakMap(data));
		$c = new WeakMap(data); // aka `c.set(new WeakMap(data))`
	};
</script>

<pre>
	mutable (count: {$a.value.get(someObj)})
	and safeMutable (count: {$b.value.get(someObj)})
	both react to changes even though their values are mutated
	and the immutable option is enabled,
	but the writable (count: {$c.get(someObj)})
	does not unless you disable the immutable option at the top of the page
</pre>

<button on:click={increment}>increment</button>
<button on:click={reset}>reset</button>

api

TODO

develop

npm i
# then
npm run dev
# or
gro dev # npm i -g @feltcoop/gro

learn more about SvelteKit, Vite, and Gro

build

npm run build
# or
gro build

deploy

Deploy (build, commit, and push) to the deploy branch, e.g. for GitHub Pages:

npm run deploy
# or
gro deploy

publish

Publish to npm:

npm run npm-publish patch|minor|major|etc
# or
gro publish patch|minor|major|etc

credits 🐢🐢🐢

SvelteSvelteKitViteesbuilduvuTypeScriptPrettierFeltGro & more

license 🐦

public domain ⚘ The Unlicense

Feel free to copypaste and modify this code however you wish; there's no need to drag a license file around with it. Attribution is appreciated but you do you.

0.1.3

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago