svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Cannot change $:-computed variable when it is computed from props

Open tenor-dev opened this issue 2 years ago • 2 comments

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

tenor-dev avatar May 31 '22 07:05 tenor-dev

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 avatar May 31 '22 08:05 gtm-nayan

@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?

tenor-dev avatar May 31 '22 15:05 tenor-dev

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.

dummdidumm avatar Nov 15 '23 12:11 dummdidumm