I am trying to create localStorage
items from an object of objects using SvelteKit.
Below you can see the code with more details:
// FILE: settings.js
// Imported by my main file +page.svelte and the Settings.svelte component. The selected property of each object is bound to a select html element inside Settings.svelte.
import { browser } from '$app/env';
import { writable} from 'svelte/store';
export const settings = writable({
speed: {
options: ['slow', 'medium', 'fast'],
// Each selected property of an object should be bound to a localStorage
// Example: speedLocal : 'slow'
selected: 'slow'
},
sound: {
options: ['on', 'off'],
selected: 'on'
},
tiles: {
options: [4, 6, 8],
selected: 4
},
theme: {
options: ['light', 'dark'],
selected: 'light'
},
})
First of all, it is not a good idea to mix mutable state with fixed values like this. There is no reason for the options to exist in a writable
store if they cannot change.
As for how to make this more convenient: You can store all values in a single object and serialize it as JSON.
The most basic example:
import { browser } from '$app/env';
import { readable, writable } from 'svelte/store';
function localStorageStore(key, initial) {
if (browser == false)
return readable(initial);
const value = localStorage.getItem(key)
const store = writable(value == null ? initial : JSON.parse(value));
store.subscribe(v => localStorage.setItem(key, JSON.stringify(v)));
return store;
}
const options = {
speed: ['slow', 'medium', 'fast'],
sound: ['on', 'off'],
tiles: [4, 6, 8],
theme: ['light', 'dark'],
}
const settings = localStorageStore('so.lss', {
speed: 'slow',
sound: 'on',
tiles: 4,
theme: 'light',
});
Since the localStorage
access is encapsulated in a function anyway, you could also easily create a store for each option individually.
If you want a generic way to use options and settings together, you can just iterate via Object.entries
, e.g.
{#each Object.entries(options) as [key, items]}
<select bind:value={$settings[key]}>
{#each items as item}
<option value={item}>{item}</option>
{/each}
</select>
{/each}
Note: The store implementation is just for illustrative purposes. You would probably want a more complex mechanism that handles:
Date
objects, as they will not be parsed back to a Date
.storage
event, if the store should be synchronized across multiple tabs.If you must keep that object format, you can also just re-map it into its components:
const configuration = {
speed: {
options: ['slow', 'medium', 'fast'],
selected: 'slow'
},
sound: {
options: ['on', 'off'],
selected: 'on'
},
tiles: {
options: [4, 6, 8],
selected: 4
},
theme: {
options: ['light', 'dark'],
selected: 'light'
},
};
const options = Object.fromEntries(
Object.entries(configuration)
.map(([key, value]) => [key, value.options])
);
const settings = localStorageStore(
'so.lss',
Object.fromEntries(
Object.entries(configuration)
.map(([key, value]) => [key, value.selected])
)
);
options
/settings
will be equivalent to the first example.