Skip to content

1.3. Application State Management with Svelte stores

Francisco Bernardo edited this page Dec 4, 2020 · 1 revision

"a store is simply an object with a subscribe method that allows interested parties to be notified whenever the store value changes." – Svelte tutorial

Svelte provides a pattern called stores for isolating values that need to be accessed by multiple components or modules. It is a way to organise and manage your application state.

Depending on your architectural design you may want to group stores according to your applications main logical areas or interfaces. In Sema, there groups of stores for Playground, Tutorial and common areas that you can find in the sema/client/stores/ folder.

If you take for instance sema/client/stores/playground.js which contains all the stores for the Playground area, you might find exported variables set to values such as writable(DEFAULT-VALUE) or storable(LOCALSTORAGE-KEY, DEFAULT-VALUE).

An example of the former is isSelectLiveCodeEditorDisabled to which the state of a UI element, the LiveCodeEditor selector, is bound.

export const isSelectLiveCodeEditorDisabled = writable(false);

An example of the later is items, which holds the current dashboard layout state in a JSON object with all widgets, their properties and values.

// Dashboard layout in items list
export const items = storable("playground", originalItems); // localStorageWrapper

Please note that items uses storable a function that extends writable to synchronize a Svelte store with your browser's local storage. This enables Sema to persist the playground data (layout, items, code, etc) across sessions.

/**
 * @storable wraps the Svelte "writable store" pattern to automatically synchronize a store with local storage 
 * * synchronize here is bidirectional which means both serializing the store value to a corresponding local storage item
 * * and reading from a local storage item and hydrating the JSON descriptors
 * ! HANDLE WITH CARE, requires a good understanding of the Svelte store mechanism, the concepts of serialisation and hydration, and local storage
 * @key text descriptor for the store in the local storage
 * @initialValue initial default value for store
 */
export function storable(key, initialValue) {
	const store = writable(initialValue); // create an underlying store
	const { subscribe, set, update } = store;

	let json = localStorage.getItem(key); // get the last value from localStorage
	if (json) {
		set(JSON.parse(json).map(item => hydrateJSONcomponent(item))); // use the value from localStorage if it exists
	}

	// return an object with the same interface as Svelte's writable() store interface
	return {
		set(value) {
			localStorage.setItem(key, JSON.stringify(value));
			set(value); // capture set and write to localStorage
		},

		update(cb) {
			const value = cb(get(store)); // passes items to callback for invocation e.g items => items.concat(new)
			this.set(value); // capture updates and write to localStore
		},

		get() {
			return localStorage.getItem(key);
			// return get(store);
		},

		subscribe, // punt subscriptions to underlying store
	};
}

Stores implement the Observer pattern and can make your application truly reactive. One of the main benefits of this design is, for instance, if you want to add a new widget to the Playground's dashboard, you just need to add to items and it will notify the necessary part of the application to update its visual state.