Reputation: 624
I tried implementing a custom Svelte store, which would allow me to "throw" one single state object at the subscribing page. Here's an example code I prepared:
import { derived, writable } from 'svelte/store'
import SomeOtherStore from '$stores/otherstore.js'
import { page } from '$app/stores'
const initialState = {
loading: true,
items: [],
param: null,
}
const createPageStore = () => {
// Create main store since derived store cannot be updated, set initial state
const mainStore = writable(initialState)
const { update } = mainStore
// instantiate other store needed to construct page state
const someOtherStore = SomeOtherStore()
function someKindOfFunctionModifyingState() {
// Need to update the state of items derived from SomeOtherStore, but the items do not exist here as the state was not propagated to the main store
update(state => {
return {
...state,
items: state.items.map(stringItem => stringItem.capitalizeFirst())
}
})
}
// combine stores in order to construct page state
const { subscribe } = derived([mainStore, page, someOtherStore], ([$mainStore, $page, $someOtherStore]) => {
const { someItems, loading } = $someOtherStore
return {
...$mainStore,
loading: $mainStore.loading || loading,
param: $page.url.searchParams.get('param'),
items: someItems,
}
})
return {
someKindOfFunctionModifyingState,
// return subscribe method from derived store
subscribe,
}
}
let instance
export default function PageStore() {
return instance ?? (instance = createPageStore())
}
mainStore is how I thought I would be able to do this, since derived stores cannot be updated
As you can see, I need to construct the example items in my state object using data provided by SomeOtherStore
.
items
array is empty in mainStore
and I don't know how to pass the resulting derived state to the mainStore
.Obviously to no surprise if I try to set the final derived state to mainStore
from within the derived store function, it results in a loop.
<script>
import PageStore from '$stores/pageStore.js'
const store = PageStore()
</script>
{#each $store.items as item}
<span>{item}</span>
{/each}
<button on:click={store.someKindOfFunctionModifyingState}>
Click to capitalize first letter
</button>
example usage in page component
TL;DR Basically my need is to be able to create single object state combined from multiple stores and be able to update that state when for example click events happen.
How can I achieve this?
Upvotes: 3
Views: 2279
Reputation: 29897
Instead of
const { subscribe } = derived(...
write
const combined = derived(...
That would allow the someKindOfFunctionModifyingState
function to use the get utility.
import { get } from "svelte/store";
function someKindOfFunctionModifyingState() {
const $combined = get(combined);
mainStore.update(($mainStore) => ({
...$mainStore,
items: $combined.items.map(stringItem => stringItem.capitalizeFirst())
}))
}
Using get
has a small performance overhead, but that's negligible for click events
Upvotes: 1