1

I'm trying to migrate from svelte4 to svelte5 and for that I would like to change some $: to $effect. A pattern that I used from time to time looks like that:

let {id} = $props();
let thing = $state();
let cache = new SvelteMap();

$effect = (async () => {
        if (!cache.has(id)) {
                cache.set(id, await get_thing(id));
        }
        thing = chache.get(id);
})

I like to update thing on id change and also whenever the cache entry is updated, because of that I like both to be reactive. However, I change cache (which is reactive) within an $effect, which I often read is a bad thing as it causes infinite loops. Is this a bad pattern? How else can I solve this?

1 Answer 1

1

The cache itself does not need to be reactive at all; whether entries exist or not should not affect anything in the UI. The items in the cache can still be reactive on their own.

thing ideally should just be a $derived depending on id, though top level async support is still experimental at this point. It then should just be:

const thing = $derived(await get_thing(id));

Where the function gets the thing from cache/updates the cache if necessary.

For now, using an $effect is probably fine, just use a regular Map or a plain object for the cache.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for your reply. How do I make the items reactive? Should each be $state()? I wanted the cache to be reactive because I used a drawer to update the thing, where the reply was put into the cache, automatically triggering an update on thing without the id being changed (while the component was never destroyed and mounted again). Another thing: I would love to use $derived() as in your solution, but what if I do error handling like giving a toast whenever await get_thing(id) fails, should it be an $effect then?
Items should be wrapped in $state which creates a reactive proxy. For error handling, you could use an $effect; if you modify state to show the toast you probably need it, since you are not allowed to modify state from within a $derived. (Top level await is experimental and needs to be enabled, it is likely buggy and requires one of the latest Svelte versions; would not necessarily recommend it right now.)
Okey, wrapping thing makes sense, but then I have to update the members of thing, it won't work to replace the whole thing object in the map, won't it? Yeah, read about the toplevel await, thanks for the link, but it needs an option to activate, rather won't do that.
Regarding the object update, you could add another level of wrapping, so at the top level there is only one property holding the entire object which then can be swapped out. You could also use a separate state for invalidation (e.g. a last changed date) so the object is updated when the id or this other state changes; the value then probably also has to be deleted from the cache at the same time. Alternatively the cache could use a composite key so different versions of the object can be stored, though maybe that is not a good idea.
Thanks for the further ideas. I thought about the further state too, mabe like state({updateCnt: 0}) and placing it into setContext(), what do you think? Alternatively, I thought I could make the one thing reactive that I currently use within the cache like let tmp = cache.get(id); cache.set(id, $state(tmp)); thing = cache.get(id); so that not to many cached objects are reactive (eventually previously fetched with fetch_many_things()), avoiding hundreds of reactive objects (would this even be an issue?)
I do not think that having many reactive objects is an issue, they only cost a bit of memory from the wrapper and do nothing except fire some signals when interacted with. As for the approach, I would recommend just experimenting to see what works best in your particular case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.