javascriptsveltesvelte-store

Why are svelte derived stores always recreated on get()?


Now this is more directed towards Svelte authors I suppose, but I just recently fully realized that derived stores are constantly recreated on get.

Example

<script>
import { derived, get, writable } from 'svelte/store'

const store = writable(0)
const derivedA = derived(store, s => {
  console.log('derivedA recreated!')
  return { name: 'A', s }
})
const derivedB = derived(derivedA, d => {
  console.log('derivedB recreated!')
  return {
    name: 'B',
    s: d.s
  }
})

function getB() {
  console.log(get(derivedB))
}
</script>

<section class="mx-4 md:mx-0">
  <button on:click={getB}>GetB</button>
</section>

I thought they'd only be recreated when the inputs would change — not every time a get is called. Especially weird is that if the derived stores are linked, the whole tree is traversed. I assumed get returned a reference to the value which, sure, you could mutate and cause all kinds of bugs if you were that foolish.

I do get that the derived stores should always return the exact same value for the same input but if someone, with no time to think too deeply about it, would depend on the derived store to only be recomputed on the original store change it would cause rather strange bugs.


Solution

  • From the docs

    Derives a store from one or more other stores. The callback runs initially when the first subscriber subscribes and then whenever the store dependencies change.

    ...you may need to retrieve the value of a store to which you're not subscribed. get allows you to do so. This works by creating a subscription, reading the value, then unsubscribing.

    In your example the derived values are nowhere used so calling get creates the first subscriber. When adding

    {$derivedA}
    {$derivedB}
    

    the logs will run on component initialization and no more when calling get