Search code examples
sveltesvelte-5

Encapsulating context interactions


The docs for the context API have this example:

import { getContext, setContext } from 'svelte';

let userKey = Symbol('user');

export function setUserContext(user: User) {
  setContext(userKey, user);
}

export function getUserContext(): User {
  return getContext(userKey) as User;
}

I suppose that the code above would live outside a component, e.g., into a svelte.js file.

Then I would import setUserContext in some component (say <ComponentA>) so that the context becomes available to that component and its whole subtree.

Then a child of <ComponentA> can import getUserContext to access the context.

Now, my question is: why does setUserContext take an argument?

Can I define it like this instead?

export function setUserContext() {
  setContext(userKey, user);
}

So that I don't need to have the user in <ComponentA> just to be able to call setUserContext.

Also, bonus question, if the context was reactive (e.g., declared with a $state rune) nothing would change right?


Solution

  • You could define context functions that do not take an argument but you have to put something into the context. setContext can only be used during component initialization so you cannot just call it again later to change the value.

    If the context is to be initialized with some default value, I would also change the function name in that case. E.g.:

    export function initializeUserContext() {
      const user = $state({ current: null });
      return setContext(userKey, user);
    }
    

    Playground example

    Here the context is set to a reactive object that does not have a current value, so it can be set later elsewhere.

    The $state matters because it turns the object into a state Proxy, which will fire signals when its current property is changed. That means $derived values and $effects will be triggered if they use it.