We often need to share data between components in any typical Svelte applications. Svelte provides a mechanism known as stores to help facilitate sharing of data. In Svelte, stores are a way of sharing data between unrelated components. While there are several types of stores, the most common is a writable store in Svelte. But how do we setup a writable store in Svelte?

A Svelte Writable Store is simply an object. Any component can subscribe to the store and when the value in a store changes, all interested components receive a notification. Since all components can write to the store, it is also known as writable store.

In this post, we will learn how to use a Svelte Writable Store to share data between components in a step-by-step manner.

1 – Svelte Store vs Custom Events

Custom events using Svelte createEventDispatcher can also perform similar functionality as a store.

So what is the benefit of using Svelte store vs Custom Events?

Svelte store is a better choice when we want to share data between unrelated components. In other words, components which may be quite far apart in the overall hierarchy. With custom events, we have to handle the event at each level. This can become a problem when there are two deeply nested components and you have to transfer some data between them.

Let’s see with the help of an example. Below is the component hierarchy of a small application that collects Book information and displays it.

svelte custom event data flow

In the above illustration, the data flow from the Add Book component to the BookList & BookDetail component has to go through the App component. In other words, the entire hierarchy comes into play. Any change in the component structure in this chain can cause significant impact to the data flow.

With Svelte stores, the sharing of data between these components becomes much easier. See below:

svelte writable store

Here, we can share data using the store. There is no need to go up to the App component and then provide the data to the BookList component.

Even at the level of such a small application, we can see the benefits of using Svelte writable stores. In a large application, this feature becomes more or less indispensable.

2 – Creating a Writable Store in Svelte Application

To utilize stores in Svelte, we need to first create a writable store.

But what is a writable in Svelte?

A writable is basically a type of store that has both set and update methods in addition to subscribe. In other words, we can read as well as write data to a writable store.

See below code where we create the store.

import { writable } from "svelte/store";

const BookStore = writable([{
    id: 1,
    title: "The Way of Kings",
    author: "Brandon Sanderson"
}]);

export default BookStore;

If you notice closely, a store is just a normal javascript file. It does not use .svelte extension. To create a store, we import the writable from svelte/store. While making a call to the writable, we can provide some initial data to the store. In this case, it is a sample book object.

3 – Subscribe to the Store

After creating a store, we have to subscribe to that store in order to read data.

<script>
    import BookStore from '../stores/BookStore.js';
    import BookDetail from './BookDetail.svelte';
    
    export let books = [];

    BookStore.subscribe((data) => {
         books = data
    })

</script>

<div class="book-list">
    {#each books as book (book.id)}
    <div>
        <BookDetail {book}/>
    </div>
    {/each}
</div>

<style>
    .book-list {
        display: grid;
        grid-template-columns: 1fr 1fr;
        grid-gap: 20px;
    }

</style>

To subscribe to the store, we have to first import the store. Next, we use the subscribe function.

Basically, this function accepts a callback that contains the current data in the store as input. We simply assign the incoming data to the books array. Whenever a new book is added, the store is updated and the callback function gets triggered.

There is also a shortcut approach for subscribing to the store. See below:

<script>
    import BookStore from '../stores/BookStore.js';
    import BookDetail from './BookDetail.svelte';
    
    //export let books = [];

    // BookStore.subscribe((data) => {
    //     books = data
    // })

</script>

<div class="book-list">
    {#each $BookStore as book (book.id)}
    <div>
        <BookDetail {book}/>
    </div>
    {/each}
</div>

<style>
    .book-list {
        display: grid;
        grid-template-columns: 1fr 1fr;
        grid-gap: 20px;
    }

</style>

As you can see, we have commented the subscription logic. Instead, we use $BookStore directly within the Svelte keyed each block.

The advantage of using $BookStore is that it automatically subscribes as well unsubscribes from the store when the component is destroyed/removed from DOM. We don’t have to worry about manually unsubscribing from the store.

4 – Update a Svelte Writable Store

Creating a store and subscribing to the store is complete.

Now, we need to see how to write/update a Svelte store. See below example:

<script>
    import Button from '../shared/Button.svelte';
    import BookStore from '../stores/BookStore.js';
    import { createEventDispatcher } from 'svelte';
    const dispatch = createEventDispatcher();

    let fields = { title: '', author: '' };
    let errors = { title: '', author: ''};
    let valid = false;

    const addBookHandler = () => {  
        if (valid) {
            let book = {...fields, id: Math.random()}
            BookStore.update(currentBooks => {
                return [book, ...currentBooks];
            })
        }
        dispatch('bookAdded')
    }

    const handleInput = (event) => {
        valid = false

        if (fields.title.trim().length < 1) {
            valid = false;
            errors.title = 'Book Title should have at least 1 character';
        }else {
            valid = true
            errors.title = '';
        }

        if (fields.author.trim().length < 1) {
            valid = false;
            errors.author = 'The Name of the Author should have at least 1 character';
        }else {
            valid = true
            errors.author = '';
        }
        
    }

</script>

<form on:submit|preventDefault={addBookHandler}>
    <div class="form-field">
        <label for="title">Book Title:</label>
        <input type="text" id="title" bind:value={fields.title} on:input={handleInput}>
        <div class="error">{ errors.title }</div>
    </div>
    <div class="form-field">
        <label for="author">Author:</label>
        <input type="text" id="title" bind:value={fields.author} on:input={handleInput}>
        <div class="error">{ errors.author }</div>
    </div>
    <Button buttonType="secondary" disabled={!valid}>Add Book</Button>
</form>

<style>
    form {
        width: 400px;
        margin: 0 auto;
        text-align: center
    }
    .form-field {
        margin: 18px auto;
    }
    input {
        width: 100%;
        border-radius: 6px;
    }
    label{
        margin: 10px auto;
        text-align: left;
    }
    .error {
        font-weight: bold;
        font-size: 12px;
        color: red;
    }
</style>

Important function here is the addBookHandler() function where we add a book to the store after performing the form validations.

After importing the BookStore, we simply call the update() function. This function also receives a callback with the current store data as input. We use Javascript’s spread syntax to add our new book to the existing store and return the updated store.

This completes the cycle by triggering the store’s subscription and updates the book list within the BookDetail component.

Conclusion

With this, we have successfully used the writable store in Svelte to transfer data between components. As you may have noticed, stores provide a great deal of flexibility when it comes to managing this data transfer.

If you have any comments or queries about this post, please feel free to mention in the comments section.

Categories: Svelte

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *