svelte
svelte copied to clipboard
Cannot change $:-computed variable when it is computed from props
Describe the bug
$: expression should track source variables and execute only when they change. This works with local component state, but not when the state is passed to another component.
This is a consistency and developer usability issue. As a developer, I expect exported variables to work exactly the same as internal ones, especially if they are not bound in parent. As a result, a lot of "strange" code needs to be written to workaround the issue.
Reproduction
https://svelte.dev/repl/1d4698e72d614e0e96450b52c33f8665?version=3.48.0
Logs
No response
System Info
Not relevant
Severity
annoyance
Line 125 of the generated code contains ($$invalidate(0, names), $$invalidate(1, hello))
, since hello
is getting invalidated, it resets names
again
Related #7416, #4933
See https://github.com/sveltejs/svelte/issues/7416#issuecomment-1088075138
@gtm-nayan it's a known issue then. We stumble upon it from time to time in our projects and it's quite frustrating. It's also VERY frustrating to Svelte beginners, because it seems illogical until you look at the generated source code. And even then it's hard to understand why compiler does this...
Svelte otherwise has an easy way to explain the reactivity - "if the compiler sees a read, it will re-run the code on change", and "this only happens on some variable assignment". But in this case, there is no obvious reason that hello
should be invalidated.
I think this wasn't an issue a while ago when I started using Svelte 3. Do we really need Svelte 4 to fix this obviously bad behavior?
This comes down to $:
standing for two things and it's not easy to distinguish:
- side effects that happen in reaction to something
- derived state that keeps values in sync
Svelte 5 fixes this by separating these into two distinct runes, $effect
and $derived
. That way it's much clearer what's going on and such edge cases are avoided entirely.
In this case, you would have a $state
variable and either update that through the binding of from an $effect
, resulting in the desired behavior.
Furthermore, due to fine-grained reactivity possibilities in Svelte 5, you can now write the state such that the binding will not update the whole array.