[svelte store][01] Learn about svelte's reactivity

About Svelte Store

[svelte store][01] Learn about svelte's reactivity[svelte store][02] svelte store's writable()[svelte store][03] Implementing StopWatch

this article describes the basic usage of svelte store.

1. Assignments

in svlete, variables declared inside a component automatically provide reactivity.

App.svelte
<script> <script> let weather = { temp: { value: 26, unit: 'C' } } const changeTemperature = (value, unit) => { weather.temp.value = value weather.temp.unit = unit || 'C' } </script>

when configuring the screen as shown below, pressing the button will execute the function changeTemperature and update the temperature from "26C" to "45F".

App.svelte
<button on:click={() => changeTemperature(45, 'F')}>change</button> <h3>{weather.temp.value}{weather.temp.unit}</h3> <!-- "26C" => "45F" -->

the key to this kind of responsiveness is "using assignment".

the code below all works the same.

TS
// OK weather.temp = {value: 45, unit: 'F'} // OK weather = { temp: {value: 45, unit: 'F'} }
  • assingment(=) is recognized as a change in the state of the leftmost variable weather.

however, if you put a value like this, the screen does not update.

1.1. Update via child property, not reactive

when looking at weather on the screen, updating via the property of the instance referenced by weather does not work reactively.

App.svelte#01
const changeTemperature = (value, unit) => { // not reactive const { temp } = weather temp.value = 45 temp.unit = 'F' console.log(weather.temp) // prints {value 45, unit: 'F'} }
  • recognized as a change to the leftmost variable temp
  • you can see the state change through the variable, but the
  • screen does not change automatically.

because we write new weather data through the variable temp, we don't recognize it as a change to the weather we see on the screen.

similarly, App.svelte#02 changes its data, but the screen doesn't update.

App.svelte#02
const { temp } = weather // not reactive const changeTemperature = (value, unit) => { temp.value = 45 temp.unit = 'F' console.log(weather.temp) // prints {value 45, unit: 'F'} }
  • recognized as a change to variable temp

if you modify the view to look at temp as follows, App.svelte#02 will work correctly.

App.svelte#02
<h3>{temp.value}{temp.unit}</h3> // updated reactively

because we're using assignment as a rule for reactivity, we can always update the screen reactively by using something like this anyway.

TS
const changeTemperature = (value, unit) => { // do some updates... weather = weather; // re-assign }
  • it's not very elegant, but it works.
  • the code above is a trick of sorts, but not a recommended practice.

1.2. Update via method, not reactive

similarly, when changing state via method inside a svelte file, the screen is not updated reactively.

App.svelte
<script> import { writable } from 'svelte/store'; let weather = { temp: { value: 26, unit: 'C' }, changeTemp(value, unit) { this.temp.value = value; this.temp.unit = unit || 'C'; } } const changeTemperature = (value, unit) => { weather.changeTemp(value, unit); } </script>
  • the state of weather has changed. but the screen is not updated.

2. Sharing through props

these rules are the same when sharing the state of a parent component to child components.

Important differences are discussed below.

share the state to the child component WeatherView as shown below.

App.svelte
<script> import WeatherView from './Weather.svelte'; let weather = { temp: { value: 26, unit: 'C' } } </script> <WeatherView {weather}/>
  • {weather} - a shorthand syntax for weather={weather}

the child component WeatherView.svelte references the weather shared by the parent, as shown below.

WeatherView.svelte
<script export let weather; </script> <span>Current: {weather.temp.value}{weather.temp.unit}</span>
  • when referencing state shared by a parent component, declare it with export let variable;.

when you change the weather in the parent component App.svelte, all of the child components (and their children) will reactively update their screens.

2.1. One-way binding

however, if a child component modifies the state shared by the parent component, the parent component will not reflect this change on the screen.

try adding functionality to the child component as follows.

WeatherView.svelte
<script> <script> export let weather; const increase = () => { // updates state of parent component weather.temp.value++; } </script> <span>Current: {weather.temp.value}{weather.temp.unit}</span> <button on:click={increase}>Change from child</button>

you have incremented the temperature in the child component through the shared variable weather.

this state change is also shared in the variable weather referenced by the parent component App.svelte.

however, the screen in App.svelte is not automatically updated.

this is called a one-way binding in svelte.

2.2. Bidirectional binding

there is a way to bypass the rules between parent and child components.

apply the bidirectional binding keyword in the parent component App.svelte as follows.

App.svelte
<script> ... </script> <WeatherView bind:weather={weather}/>
  • bind:prop={value}` - prop matches the variable name declared in the child component. and the value is the variable in the current component.

with this syntax, the weather variable weather is treated as if it were a global variable.

whether you change it in the parent or the child component, they both share this state change and the screen is updated.

3. the need for a svelte store

while the screen update rule via assignment seems convenient, there are a lot of inconveniences with it.

3.1. Breaks ownership of state

as in the example in [2.1. One-Way Bindings], allowing a child to arbitrarily change state shared by a parent breaks the ownership of the state, making it harder to trace the change (meaning harder to debug)

to compensate for this drawback, the svelte store provides a readable function.

SVELTE
import { readable, writable } from 'svelte/store';
  • this is typically used to wrap state in writable and share state as read-only to other components.

3.2. Programming Constraints

if the screen can only be changed via assignment, you'll have to do the following trick for every state change.

Svelte
const changeTemperature = (value, unit) => { weather = weather; // re-assignment }
  • when updating an element of an array
  • when changing the state via sub property
  • when changing the state by calling a method

to keep your view as simple as possible, you should isolate the functions that manage state in a separate class, and the view should be updated reactively through method calls and state retrieval.

The less logic in the view, the better.

reactivity via assignment is a useful feature, but it's not enough to keep the code lean.

we use the features provided by the svelte store to implement models and views concisely.

About Svelte Store

[svelte store][01] Learn about svelte's reactivity[svelte store][02] svelte store's writable()[svelte store][03] Implementing StopWatch