Is this a bug or is this a feature of Svelte reactivity statements?
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 ---"
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
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, Yep Not only binding, passing too
Can you elaborate and provide a REPL? You're saying you see unnecessary updates without using bind: at all?
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
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.
Seems closely related to this https://github.com/sveltejs/svelte/issues/7129#issuecomment-1012533937
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
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
This will be fixed in Svelte 5 through tracking dependencies more correctly at runtime