svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Is this a bug or is this a feature of Svelte reactivity statements?

Open siroBS opened this issue 3 years ago • 8 comments

Describe the bug

App.svelte:

<script>
  import Child from './Child.svelte';
	
  let data = {};	
  let dataNew = {};
</script>

<Child
  bind:data
  bind:dataNew
/>

Child.svelte:

<script>
  export let data;
  export let dataNew;
	
  $: if (data) {
    console.log('--- WTF? ---');
  }
	
  $: if (data) {
    console.log('--- WTF Trigger ---');
    dataNew = {};
  }

</script>

<button on:click={()=>{console.log('Clicked');dataNew=dataNew}}>
  Click me
</button>

Output on button click:

"Clicked"
"--- WTF? ---"
"--- WTF Trigger ---"

Related StackOverflow

It seems that this only happens when passing (binding) a non primitive values (objects, arrays). The data of "data" not changed, why both statements have been triggered in Child.svelte component? Looks like a bug...

Reproduction

https://svelte.dev/repl/87031a5efc554b7fba7ea177b6b30b81?version=3.49.0

Logs

No response

System Info

Svelte version: 3.49.0

Severity

annoyance

siroBS avatar Aug 03 '22 08:08 siroBS

https://github.com/sveltejs/svelte/issues/5689

Even if you remove the button, you get two logs, which is the original bug. Svelte triggers updates for no reason when binding objects.

Prinzhorn avatar Aug 03 '22 08:08 Prinzhorn

Prinzhorn, Yep Not only binding, passing too

siroBS avatar Aug 03 '22 09:08 siroBS

Can you elaborate and provide a REPL? You're saying you see unnecessary updates without using bind: at all?

Prinzhorn avatar Aug 03 '22 09:08 Prinzhorn

Can you elaborate and provide a REPL? You're saying you see unnecessary updates without using bind: at all?

Please, Issue with passing args to child

siroBS avatar Aug 03 '22 09:08 siroBS

Thanks, this looks like a different bug to me (there might already be a duplicate issue though).

For some reason Svelte invalidates the expression used in the condition. If you do this:

-	$: if (data) {
+	$: if (true) {
		console.log('--- WTF-Trigger---');
		dataNew = [{key: 1, val: '1'}];
	}

then nothing is logged. Which doesn't make sense, because data is const throughout the entire application. It is never changed, but Svelte invalidates it anyway.

Prinzhorn avatar Aug 03 '22 09:08 Prinzhorn

Seems closely related to this https://github.com/sveltejs/svelte/issues/7129#issuecomment-1012533937

Prinzhorn avatar Aug 03 '22 09:08 Prinzhorn

Interesting, with a primitive attributes does nothing going on like this. Looks like a hidden linking (bounding) under the hood, that causes Svelte to track passed arguments as a single object. And what is even more interesting is that if we change in the click handler to the SAME assignment (even though the value of the ENTIRE object "data" also changes), but the object is not itself, the problem also disappears. Unfortunately, I don't have the resources to dive further into the problem and do research, but this may be the root of the problem. REPL

siroBS avatar Aug 03 '22 11:08 siroBS

In case it might be useful: here's another eample where this issue manifests itself:

  • StackOverflow question: https://stackoverflow.com/questions/73432013/trying-to-understand-why-changing-a-variable-re-triggers-reactive-statements-of
  • REPL demonstrating the issue: https://svelte.dev/repl/d07ffc88e4a34cb797d2ceb6ba0ec6a4?version=3.49.0

According to a comment on the linked stackoverlow question, this bug seems to have been introduced after the 3.2.0 release: the same eample linked above works with that version: https://svelte.dev/repl/d07ffc88e4a34cb797d2ceb6ba0ec6a4?version=3.2.0

Edit: the bug can be reproduced with 3.2.1, so that's where it was probably introduced: https://github.com/sveltejs/svelte/compare/v3.2.0...v3.2.1

jawher avatar Aug 21 '22 17:08 jawher

This will be fixed in Svelte 5 through tracking dependencies more correctly at runtime

dummdidumm avatar Nov 15 '23 12:11 dummdidumm