Storing Shared State in a Svelte App

John Au-Yeung - Jan 28 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Svelte is an up and coming front end framework for developing front end web apps.

It’s simple to use and lets us create results fast.

In this article, we’ll look at how to store shared states in a Svelte store.

Writable Stores

We can add a writable store to store data that are shared by multiple components.

For instance, we can write the following to create a store and use it in a component:

App.svelte :

<script>
  import { count } from "./stores.js";

  let countValue;

  const unsubscribe = count.subscribe(value => {
    countValue = value;
  });

  const increment = () => {
    count.update(n => n + 1);
  };
</script>

<button on:click={increment}>Increment</button>
<p>{countValue}</p>
Enter fullscreen mode Exit fullscreen mode

stores.js :

import { writable } from "svelte/store";

export const count = writable(0);
Enter fullscreen mode Exit fullscreen mode

In the code above, we created a shared writable state in stores.js called count .

Then we subscribed to it to get the latest value and set it to count_value .

We can also call the set method on count to reset the value as follows:

App.svelte :

<script>
  import { count } from "./stores.js";

  let countValue;

  const unsubscribe = count.subscribe(value => {
    countValue = value;
  });

  const increment = () => {
    count.update(n => n + 1);
  };

  const reset = () => {
    count.set(0);
  };
</script>

<button on:click={increment}>Increment</button>
<button on:click={reset}>Reset</button>
<p>{countValue}</p>
Enter fullscreen mode Exit fullscreen mode

In the code above, we run count.set(0) when we click on Reset to set the count state to 0.

We have to call unsubscribe on the store when the component is destroyed so that we can free up the resources used for the subscription of states.

For instance, we can modify the example above as follows to do that:

App.svelte :

<script>
  import { count } from "./stores.js";
  import { onDestroy } from "svelte";

  let countValue;

  const unsubscribe = count.subscribe(value => {
    countValue = value;
  });

  const increment = () => {
    count.update(n => n + 1);
  };

  onDestroy(unsubscribe);
</script>

<button on:click={increment}>Increment</button>
<p>{countValue}</p>
Enter fullscreen mode Exit fullscreen mode

The code above calls unsubscribe the function in the destroy portion of the component lifecycle so that the count state is no longer subscribed to.

We can replace the store state subscription with the $ . For instance, we can shorten the above example to:

<script>
  import { count } from "./stores.js";
  import { onDestroy } from "svelte";

  const increment = () => {
    count.update(n => n + 1);
  };
</script>

<button on:click={increment}>Increment</button>
<p>{$count}</p>
Enter fullscreen mode Exit fullscreen mode

We eliminated the code to subscribe and unsubscribe from the count store and the component still works the same as before.

Read Only Stores

We can make a store that’s read only with the readable function.

For instance, we can write the following code to make a counter state that’s subscribed to by a component as follows:

stores.js :

import { readable } from "svelte/store";

export const count = readable(0, set => {
  let count = 0;
  const interval = setInterval(() => {
    set(count);
    count++;
  }, 1000);

  return () => {
    clearInterval(interval);
  };
});
Enter fullscreen mode Exit fullscreen mode

App.svelte :

<script>
  import { count } from "./stores.js";
</script>

<p>{$count}</p>
Enter fullscreen mode Exit fullscreen mode

In the code above, we call the readable function in the store. In the second argument, we pass in a callback with the set function as the parameter, which is called to update the store’s value.

We did that in the setInterval callback. Then we return another function, which is used to clean up the code when the store is unsubscribed to.

When we run the code, we see a number that’s incremented by 1 every second on the screen.

Store with Derived State

We can call the derived function to create a store that’s derived from another state.

For instance, we can create a store that emits a value that’s double the count state that we have above as follows:

stores.js :

import { readable, derived } from "svelte/store";

export const count = readable(0, set => {
  let count = 0;
  const interval = setInterval(() => {
    set(count);
    count++;
  }, 1000);

  return () => {
    clearInterval(interval);
  };
});

export const doubleCount = derived(count, $count => $count * 2);
Enter fullscreen mode Exit fullscreen mode

App.svelte :

<script>
  import { count, doubleCount } from "./stores.js";
</script>

<p>{$count}</p>
<p>{$doubleCount}</p>
Enter fullscreen mode Exit fullscreen mode

In the code above, we have the doubleCount store, which is created by calling the derived function with the count store passed in as the first argument and a callback which returns a value that we want to derive from the value emitted by count as the second argument.

Then we see a number that’s incremented by 1 every second and another that’s incremented by 2 every second.

Store Bindings

We can bind directly to the value of the store if the store is writable.

For instance, we can use bind:value to bind to the store to the inputted value directly and manipulate the store’s value as follows:

store.js :

import { writable } from "svelte/store";

export const name = writable("");
Enter fullscreen mode Exit fullscreen mode

App.svelte :

<script>
  import { name } from "./stores.js";
</script>

<input bind:value={$name}>
<button on:click="{() => $name += '.'}">
  Add period
</button>
<p>{$name}</p>
Enter fullscreen mode Exit fullscreen mode

In the code above, we change the value of $name as we type in something in the input, and also when we click Add period.

Then we display the $name store’s value in the p element.

Conclusion

A store is handy for storing shared states. We make stores that are read-only, writable or are derived from another store.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .