DEV Community

Cover image for Svelthursday: Nested Store Values
a Mediocre Dev
a Mediocre Dev

Posted on

Svelthursday: Nested Store Values

What is this?

Having recently fallen in love with Svelte, I have dove into it on the deep end. And let me tell you, it is awesome! With these short posts, I aim to explore some of the less covered topics that I encounter as I write stuff in Svelte.

Example repository can be found here.

Alright, it's time to dig in... Thirty seconds, go!

Nested Store Values

A lot of Svelte examples show you how to handle stores that contain primitive values or a simple object. But how about arrays of objects? Or deeply nested objects?

Tip: Svelte stores are powerful, and it might be tempting to put everything in a single store. Better practice, however, is to split larger data objects into separate stores and combine them using derived stores. Read on!

Say we want to create a shopping cart modal that iterates over an array of products in our shopping cart, like so:

Shopping Cart Example

We start off with a store containing all the products.

const cart = writable([{
    name: "Nintendo Switch OLED 2021",
    image: "/images/switch.webp",
    description: "Nintendo Switch with OLED-Display, 64GB internal storage",
    sku: "0934192",
    price: 499,
    count: 1
}, {
    name: "Metroid Dread",
    image: "/images/dread.webp",
    description: "Hottest Nintendo Switch game",
    sku: "10329362",
    price: 59.98,
    count: 1
}]);
Enter fullscreen mode Exit fullscreen mode

Notice we have a count property. Maybe we want the customer to be able to change this value and sync this value with the store. Easy enough:

<ol type="list">
    {#each $cart as { sku, image, description, name, count, price } (sku)}
    <li>
        <a href="/products/{sku}"><img src="{image}" alt="{name} product image"/></a>
        <a href="/products/{sku}"><h4>{name}</h4></a>
        <p>${price}</p>
        <p>{description}</p>
        <div>
            <span>Qty:</span>
            <input bind:value="{count}" type="number" min="0" />
        </div>
    </li>
    {/each}
</ol>
Enter fullscreen mode Exit fullscreen mode

The bind:value="{count}" is what does the magic. This will reactively update the store for us as we change the value, just like we would expect.

Hold up!

There is one gotcha here at the moment. Say for some silly reason we want to use this as a template and dynamically bind the input to either price or quantity:

<ol type="list">
    {#each $cart as { sku, image, description, name, ...rest } (sku)}
    <li>
        <a href="/products/{sku}"><img src="{image}" alt="{name} product image"/></a>
        <a href="/products/{sku}"><h4>{name}</h4></a>
        <p>${rest.price}</p>
        <p>{description}</p>
        <div>
            <input bind:value={rest["price"]} type="number" min="0" />
        </div>
    </li>
    {/each}
</ol>
Enter fullscreen mode Exit fullscreen mode

So we use the spread operator to gather the dynamically allocated variables and index into the price property. Our input suddenly becomes unresponsive. To circumvent this we can actually further nest those values in our store like so:

...
cart: {
    count: 1,
    price: 499
}
Enter fullscreen mode Exit fullscreen mode

And index into it in the input like so:

<input bind:value={rest.cart["price"]} type="number" min="0" />
Enter fullscreen mode Exit fullscreen mode

And now our input works again.

I have filed a bug report about this.

Finally, lets get the total.

Derived, derived, derived

A derived store in Svelte can take one or more stores and combine them. In a future post on stores I will look at how you combine several stores. But for now lets see a basic use case.

To get the total (quantity multiplied by price, summed) we can use a derived store like so:

const total = derived(cart, (cart) =>
    cart.reduce((acc, item) => {
        return acc + item.price * item.count;
    }, 0)
);
Enter fullscreen mode Exit fullscreen mode

The first parameter is the store or collection of stores we want to pass into the derived store, and the second argument a callback function that takes the values of each passed store as an array, and the set method as a second argument. The callback then returns the derived value (and can alternately alter the passed stores through the set function).

This makes derived stores extremely powerful constructs. And the example above barely scratches the surface by returning the accumulated sum of the price multiplied by quantity using the reduce method.

That's it! You made it this far. Hope you learned something, and chime off in the comments or message me if there is anything you want to learn more about in Svelte, or if you have discovered some cool techniques with stores.

Have a great Svelthursday!

Discussion (0)