Combining the Context API with Stores in Svelte
Published on July 10, 2020See this Repl for a working example of this code
Svelte’s Context API is a bit of a misunderstood feature that a lot of users struggle with. The core concept of it however is very simple: it allows you to create a key-value store that is only accessible to the current component and all it’s descendants. This is a very nice feature to have if you work with component composition and the use of slots as I will discuss further down.
How to use the Context API
This feature comes with two functions: setContext
and getContext
, which as their names suggests, set something and get something (preferable the same thing), they are part of the svelte
package and can be imported from there:
<script>
import { setContext } from 'svelte'
setContext('key', value) // Add the key/value pair
</script>
And in one of the components that are a descendant of this component
<script>
import { getContext } from 'svelte'
const value = getContext('key') // Gets the value stored as key
</script>
Why use the Context API ?
When dealing with children we can always just pass the values as properties, but this would mean that if a variable has to be use deeper into the nesting tree it’s has to be passed around through a series of components adding extra overhead to each of those intermediate components. Since the context is available to all descendants using it here will eliminate the need of passing the property around.
Another place this can be a real saviour is when dealing with component composition and slots where the children are completely unknown to the current component. They key/value pairs of the Context API are however fully available from within the slotted content as they are a descendant of the component.
Making the Context Reactive
Having the value not change anymore is however not what you always want, luckily enough the solution for this as simple as using Svelte: pass a store to the context
<script>
import { setContext } from 'svelte'
import { writable } from 'svelte/store'
const value = writable(123)
setContext('key', value)
</script>
and somewhere else
<script>
import { getContext } from 'svelte'
const value = getContext('key')
<script>
<span>value: {$value}</span>
Notice here that since it’s a store you have to use the $
notation to get the value.
Tracking the initial value
We can use this to keep track of the initial values of a component and provide the user with a Reset opportunity. Take the following code
<script>
import { getContext, setContext } from 'svelte'
import { writable } from 'svelte/store'
import Child from './Child.svelte'
let name = 'world';
let nameStore = writable(name)
$: nameStore.set(name)
setContext('initial', name)
setContext('name', nameStore)
const reset = () => name = getContext('initial')
</script>
<input bind:value={name}>
<Child />
<button on:click={reset}>Reset</button>
<script>
import { getContext } from 'svelte'
const name = getContext('name')
const initial = getContext('initial')
</script>
<div>
<p>Initial value: {initial}</p>
<p>Current value: {$name}</p>
</div>
In this little program the user will be shown an inputfield, reset button and two lines of text: one with the initial value and one with what is currently entered in the input box. When we did setContext('initial', name)
we entered the current value of name into the context and as said before: context values are not reactive which means this value will never change anymore, this makes it possible to recall this value and make the reset work.