Handling large nested objects with writable stores, performance concerns

536 Views Asked by At

I have a writable store

export default writable({})

My server is constantly pushing updates to this store and those handlers are in a js file

const handlers = {
    newUser: async (user) => {
        userStore.update(user => {
            userStore[user.id] = user;
            return userStore;
        });
    },
    statusChange: ...
    more...
};

A user object has multiple nested objects and arrays. My users page has a #each loop, and for every user, it passes the user to the <User /> component. The user component then passes other nested objects to other components, etc. The issue I'm having is that when I change a deeply nested value, the hundreds of user objects and the components they're used in re-run their reactive statements. Since the values mostly haven't changed, most of them won't rerender, but I have heavy code that needs to run on change, and this code is getting re-ran even if the component instance relates to a user that hasn't changed. I tried having nested stores, but then my handlers need to get(userStore) every single update, and so on for nested stores until I reach the one that actually was meant to be updated. Another issue with this is: it didn't solve the problem of updates down the chain when the change happens at the top level, ie adding a new key: {user} to the userStore.

So is there a proper way to do this kind of state management, without causing updates where there are no changes?

Thank you.

1

There are 1 best solutions below

2
brunnerh On

The one thing I can think of is using <svelte:options immutable /> and derived stores.

By making components immutable, you get more optimized and limited updates and derived stores introduce their own change tracking, so if you pull out a property of an object, any other changes to the object will not trigger that store's subscribers.

E.g. assume the user store has a name property, you can decouple it a component like this:

<svelte:options immutable />
<script>
    import { derived } from 'svelte/store';
    
    export let user;
    
    const name = derived(user, $user => $user.name);
    $: $name, console.log('Name changed.');
</script>

<div>
    Name: {$name}
</div>

Likewise, instead of applying {#each} directly to any nested property, you can pull it out and separate it from the other changes.

More complex REPL example (Try removing immutable and/or the derived stores.)