I've followed along with the great Svelte tutorial but I'm having trouble understanding when I should use derived
in my custom stores. In this example, I'm creating a game with 3 states:
PRE_GAME
IN_GAME
POST_GAME
I want to return a boolean check for when I'm in one of those states, and I think it would be best on the custom store itself.
This is my current code:
import { writable, derived } from 'svelte/store';
export const gamestate = (() {
const { set, subscribe } = writable('PRE_GAME');
return {
subscribe,
set
};
})();
export const preGame = derived(gamestate, ($gamestate) => $gamestate === 'PRE_GAME');
export const inGame = derived(gamestate, ($gamestate) => $gamestate === 'IN_GAME');
export const postGame = derived(gamestate, ($gamestate) => $gamestate === 'POST_GAME');
Is it possible to move the derived methods onto the gamestate
store like gamestate.preGame()
? Does that make sense to do in Svelte? Then I can call $gameState
and get whichever value, but also return a boolean check when I need an explicit value.
I expected to be able to do check the internal value without needing to derive its value. Maybe something like below, but it always returns false because gamestate
is a writable object.
export const createStore = (() {
const { set, subscribe } = writable('PRE_GAME');
return {
subscribe,
set,
preGame: () => gamestate === 'PRE_GAME',
inGame: () => gamestate === 'IN_GAME',
postGame: () => gamestate === 'POST_GAME',
};
})();
What am I misunderstanding here?
What you do is return a subscription to a derived store in your custom store:
import { derived, writable } from 'svelte/store'
export const gameState = (() => {
const store = writable('PRE_GAME')
const store2 = derived(store, $store => ({
preGame: $store === 'PRE_GAME',
inGame: $store === 'IN_GAME',
postGame: $store === 'POST_GAME'
}))
return {
set: store.set,
subscribe: store2.subscribe
}
})()
Now when you do set
on the store it will set the value on store, then store2 will derive new values and the subscribers will react to those changes:
<script>
import { gameState } from "./store.js"
</script>
<button on:click="{() => $gameState='PRE_GAME'}">go PRE_GAME</button>
<button on:click="{() => $gameState='IN_GAME'}">go IN_GAME</button>
<button on:click="{() => $gameState='POST_GAME'}">go POST_GAME</button>
<br />
{#if $gameState.preGame}
<span>PRE_GAME</span>
{/if}
{#if $gameState.inGame}
<span>IN_GAME</span>
{/if}
{#if $gameState.postGame}
<span>POST_GAME</span>
{/if}
You can also define different getters
for each specific state:
import { derived, writable } from 'svelte/store'
export const gameState = (() => {
const store = writable('PRE_GAME')
const { set, subscribe } = store
return {
set,
subscribe,
get preGame() { return derived(store, $store => $store === 'PRE_GAME') },
get inGame() { return derived(store, $store => $store === 'IN_GAME') },
get postGame() { return derived(store, $store=> $store === 'POST_GAME') },
}
})()
Now the only problem is that doing $gameState.preGame
does not exists because that tries to get the preGame prop from the content of $gameState, and doing gameState.$preGame
or similar is invalid syntax. To get around that you can destructure the props out of the store where you need them:
<script>
import { gameState } from './store.js'
const { preGame, inGame, postGame } = gameState
</script>
<span>Current State: {$gameState}</span>
{#if preGame}<span>PRE_GAME</span>{/if}
{#if inGame}<span>IN_GAME</span>{/if}
{#if postGame}<span>POST_GAME</span>{/if}
Custom methods on the stores are for how you mutate the state (set and update), but stores generally are only have one getter: the state. And I don't think you want it to be otherwise.
From here on, I see three solutions :
{ gameSate: 'PRE_GAME', isPreGame: true, isInGame: false, isPostGame: false }
and then use a custom update function.get()
).I would decide between just by pure DRYness: imports vs. repeating the clause.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With