You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte/documentation/docs/06-runtime/01-stores.md

5.5 KiB

title
Stores

[!NOTE] Prior to the introduction of runes, stores were the primary state management mechanism for anything that couldn't be expressed as component state or props. With runes — which can be used in .svelte.js/ts files as well as in components — stores are rarely necessary, though you will still sometimes encounter them when using Svelte. See When to use stores below.

A store is an object that allows reactive access to a value via a simple store contract. The svelte/store module contains minimal store implementations that fulfil this contract.

Inside a component, you can reference the store's value by prefixing it with the $ character. (You cannot use this prefix to declare or import local variables, as it is reserved for store values.)

The component will subscribe to the store when it mounts, and unsubscribe when it unmounts. The store must be declared (or imported) at the top level of the component — not inside an if block or a function, for example.

If the store is writable, assigning to (or mutating) the store value will result in a call to the store's set method.

<script>
	import { writable } from 'svelte/store';

	const count = writable(0);
	console.log($count); // logs 0

	count.set(1);
	console.log($count); // logs 1

	$count++;
	console.log($count); // logs 2
</script>

Store contract

To be considered a store, an object must implement the Readable interface. It can additionally implement the Writable interface. Beyond that, a store can include whatever methods and properties it needs.

Readable stores

A readable store is an object with a subscribe(fn) method, where fn is a subscriber function. This subscriber function must be immediately and synchronously called with the store's current value upon calling subscribe, and the function must be added to the store's list of subscribers. All of a store's subscribers must later be synchronously called whenever the store's value changes.

The subscribe method must return a function which, when called, removes the subscriber.

// @filename: index.ts
import type { Readable } from 'svelte/store';
const readable = {} as Readable<any>;
// ---cut---
const unsubscribe = readable.subscribe((value) => {
	console.log(value);
});

// later
unsubscribe();

[!NOTE] Advanced: if a second argument is provided to subscribe, it must be called before the subscriber itself is called. This provides a mechanism for glitch-free updates.

Writable stores

A writable store is a readable store with set and update methods.

The set method takes a new value...

// @filename: index.ts
import type { Writable } from 'svelte/store';
const writable = {} as Writable<string>;
// ---cut---
writable.set('new value');

...while the update method transforms the existing value:

// @filename: index.ts
import type { Writable } from 'svelte/store';
const writable = {} as Writable<string>;
const transform = (value: string) => value;
// ---cut---
writable.update((value) => transform(value));

Generally, these methods will have a synchronous effect but this is not required by the contract — for example in the case of spring and tweened it will set a target value, and subscribers will be notified in requestAnimationFrame callbacks.

RxJS interoperability

For interoperability with RxJS Observables, the subscribe method is also allowed to return an { unsubscribe } object rather than the function itself.

Note that unless observable.subscribe(fn) synchronously calls fn (which is not required by the Observable spec), Svelte will see the value of $observable as undefined until it does.

Creating custom stores

In general, you won't be implementing the subscription logic yourself. Instead, you will use the reference implementations in svelte/store even if you expose a custom interface (demo):

<!--- file: App.svelte --->
<script>
	import { writable } from 'svelte/store';

	function createCounter() {
		const { subscribe, set, update } = writable(0);

		return {
			subscribe,
			increment: () => update((n) => n + 1),
			decrement: () => update((n) => n - 1),
			reset: () => set(0)
		};
	}

	const counter = createCounter();
</script>

<h1>count: {$counter}</h1>
<button onclick={counter.increment}>increment</button>
<button onclick={counter.decrement}>decrement</button>
<button onclick={counter.reset}>reset</button>

When to use stores

Stores are still a good solution when you have complex asynchronous data streams or it's important to have more manual control over updating values or listening to changes. If you're familiar with RxJs and want to reuse that knowledge, the $ also comes in handy for you.