Running a function when a value changes in Svelte

Published

Reactivity is a central tenet of the Svelte framework. Its compiler works hard behind the scene so that you can focus on your code instead of tracking your application’s state yourself.

But while a simple state change is pretty simple and well covered by the documentation, sometimes you need to plug into this reactive magic to bring out side effects, so let’s check out a few ways you can do this.

Using a simple variable

This first way is actually quite simple: As we briefly touched upon in the introduction, the Svelte compiler will work out the dependency flow of your application’s state. If something changes, then its dependants will have to change.

We’re hacking our way into making sure the compiler believes this variable is necessary for the function we want to run.

<script>
    // input is reactive as it is bound to the input field
    let input = "";
    let transformed;

    // This anonymous function is defined and executes as part of a reactive statement
    $: (() => {
        // the svelte compiler will infer that this anonymous function needs to run every time input changes
        transformed = `transformed input: ${input}`
    }) ()
</script>

<input bind:value={input}>
<div>{transformed}</div>

Let’s break it down further:

<script>
    // input is reactive as it is binded to the input field
    let input = "";

    $: (() => {
        // input is not actually used but present as a statement
       (input);
       console.log("This function is running");
    }) ()
</script>

<input bind:value={input}>

And there you go, you now have a side effect on every change. I do not recommend this at all but still, now you know a bit more about reactivity in Svelte.

Using events

Since we’ve just showcased something using a HTML input, let’s go a bit further and see how we can just hook in the oninput to run a function whenever there’s a change.

<script>
    function runMeOnInput(onInputEvent) {
        console.log("This function is running");
    }
</script>

<input on:input={runMeOnInput}>

Notice that this on:input event does not provide us with the current value of the input, only with the latest change.

Using stores

And lastly, let’s have a look at how we would implement such a behaviour using Svelte’s built-in stores:

<script>
	import { writable } from "svelte/store";
    // Let's just create a writable store
    const input = writable("start");

    // And now we can just subscribe on any change happening to this value, we get the current value as a bonus
	input.subscribe((value) => {
		console.log(`This function is running, current input is ${value}`);
	})
</script>

<!-- Let's bind the input field value to the store's value -->
<input bind:value={$input}>

As you can probably guess, this is my preferred and recommended way.

You know a value is going to be reactive and you know that you’ll want to act on its value every time it changes. Stores and subscription make this a breeze, it’s pretty readable, you get the current value and there’s no need to hack into Svelte’s reactive model.

#svelte#reactive