javascriptsveltesvelte-storesvelte-5

Svelte 5 reactive store methods


The below code is working as it should, I'm just trying to gain a better understanding of why.

How is the getCurrentElement method of TestState reactive without the use of $derived()? Are store methods that make use of state reactive by default?

Im using svelte@5.0.0-next.217

test-store.svelte.ts

import { getContext, setContext } from 'svelte';

class TestState {
  elements = [
    {
      id: 'hghxt',
      name: 'Give',
    },
    {
      id: 'vhtl9',
      name: 'Connection',
    },
    {
      id: '5n0t0',
      name: 'Take notice',
    },
    {
      id: 'xlfba',
      name: 'Keep learning',
    },
    {
      id: '1f3z2',
      name: 'Be active',
    },
  ];

  #currentElementId = $state<string>();
  
  getCurrentElement() {
    return this.elements.find(element => element.id === this.#currentElementId);
  }

  setCurrentElement(id: string) {
    if (!this.elements.some(element => element.id === id)) return;

    this.#currentElementId = id;
  }
}

const TEST_STORE_KEY = Symbol('TEST_STORE');

export function setTestState() {
  return setContext(TEST_STORE_KEY, new TestState());
}

export function getTestState() {
  return getContext<ReturnType<typeof setTestState>>(TEST_STORE_KEY);
}

TestComponent.svelte

<script lang="ts">
  import { getTestState } from '$lib/stores/test-store.svelte';

  // initialised at +page.svelte
  const testState = getTestState();
</script>

{#each testState.elements as { name, id }}
  <button 
    class:bg-yellow={testState.getCurrentElement()?.id === id} 
    onclick={() => testState.setCurrentElement(id)}>
    {name}
  </button>
{/each}

Solution

  • In your "HTML code" you call testState.getCurrentElement(). When you do that, Svelte will notice which reactive variables that call is dependent on. In getCurrentElement() you access this.#currentElementId, so that way Svelte knows that this part of the "HTML code" needs to be re-rendered/updated when this.#currentElementId is assigned a new value.

    You only need to use $derived() when you want to automatically compute a new value as soon as a reactive variable changes value, and that's not the case for you here, so here I don't see any point in using $derived() here.


    @JackTempleman, maybe we call things differently, but there is no such thing as a reactive function, at least not in my vocabulary. We only have reactive variables/expressions, which can be created using some runes, like $state(). If one of your function uses a reactive variable/expression, then that function itself is not reactive; it's just a function that uses reactive variables/expressions. Only creating such a function will not cause anything to recompute on its own. But if you call/use such a function at some places where Svelte tracks reactivity (like in the HTML code in a component or in some runes), then Svelte will become aware of the reactive variables/expression the function uses, and Svelte will call the function again in the future when those reactive variables/expressions changes value.