Published 05-21-2023
Summary
For context, if you’re new to svelte, a very basic example of reactive statements
let a = 1
let b = 1
$: c = a + b; // 2
a = a + 1 would reactively update c to 3
And without reactivity, c would still be equal to 2.
Any top-level statement can be made reactive by prefixing it with the
$:JS label syntax. Reactive statements run after other script code and before the component markup is rendered, whenever the values that they depend on have changed. Source
“Layering” reactive statements can change when subsequent reactive statements execute. (layering is not a technical or official term)
For example, if I want to fetch some data only when my notebook_id changes, I can layer my reactive statements like this.
export let data;
$: ({notebook_id} = data)
$: { loadData(notebook_id) } // Fires when *id* changes
This code will only execute when the notebook_id value changes. In other words, unrelated changes to data, won’t cause loadData to fire
Context: A
+page.sveltefile can have a sibling+page.tsthat exports a load function, the return value of which is available to the page via thedataprop Source
For contrast, the code below, would execute loadData every time data changes, even if notebook_id has the same value
export let data;
$: { loadData(data.notebook_id) } // Fires when *data* changes
Here is a similar example using stores and reactive statements
A store is simply an object with a subscribe method that allows interested parties to be notified whenever the store value changes Source
In this example, we’re calling refreshEditor whenever the value prop on notebook_store changes.
import { notebook_store } from 'notebook'
export let data;
$: ({notebook_id} = data)
$: active_notebook = $notebook_store[notebook_id];
$: current_value = active_notebook?.value
$: if (current_value) {
// Only executes when current_value changes
refreshEditor(current_value);
}
For contrast, the following statement would execute when any property on notebook_store updates.
$: if ($notebook_store.current_value) {
refreshEditor($notebook_store.current_value);
}
If refreshEditor is expensive (in my real-world use-case it’s updating a CodeMirror instance) we only want to run it when we need to.
Not realizing this was the case when I first started using Svelte-Kit, I had littered my app with statements like this. (don’t do this)
let last_notebook_id = ''
$: {
if (data.notebook_id && last_notebook_id != data.notebook_id) {
loadData(data.notebook_id)
last_notebook_id = data.notebook_id
}
}
Aside from being much more verbose, I think this code is more error prone
When you navigate around your application, Svelte-Kit reuses existing layout and page components. Source
i.e. Svelte-Kit will re-use the page instead of destroying and recreating it. While the example above is probably okay, I often worry I’ll introduce some hard-to-find bug when I write code like this in my pages.
+page.ts load functions work in a similar way on navigation.
Svelte-Kit tracks the dependencies of each load function to avoid re-running it unnecessarily during navigation. Source
The code below will re-execute during navigation if params.notebook_id has changed.
So for example, if we navigated from notebook/123 to notebook/456 the load function would execute. Because notebook_id has changed.
But going from notebook/456 to notebook/456/child-route would not re-execute.
export const load = ({ params }) => {
return {
notebook_id: params.notebook_id,
streamed: {
notes: fetchNotes(params.notebook_id)
}
};
};
This works without any sort of “layering”. In other words, this executes on navigation when notebook_id changes. Not when any params property changes
WITNESS MY BEAUTIFUL REACTIVITY PASSED DOWN THE ARMSTRONG FAMILY LINE FOR GENERATIONS! — Major Alex Louis Armstrong