svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Svelte 5: $derived rune issue when destructuring

Open HighFunctioningSociopathSH opened this issue 1 year ago • 3 comments

Describe the bug

Reactivity is triggered for unchanged destructured properties of a derived object even though they weren't changed.

Reproduction

<svelte:options runes />

<script lang="ts">
  let mainObject = $state({
    number: 1,
    object: {},
    string: "hello"
  });

  let derivedObject = $derived(mainObject);

  setTimeout(() => {
    mainObject.number = 2;
  }, 3000);

  $inspect("derivedObject", derivedObject); // Works as expected and logs again after 3 seconds since a property inside
  // the mainObject was changed.

  $inspect("derivedObject.string", derivedObject.string); // Works as expected and DOES NOT log after 3 seconds since the 
  // property that was changed on the mainObject was `number` and not `string`

  let { number, object, string } = $derived(mainObject);

  $inspect("destructed string property", string); // I'm expecting this to be equivalent to the second $inspect 
  // which didn't log derivedObject.string but if destructured, and then logged, this last $inspect
  // will run again after 3 seconds even though just like the second $inspect `string` property wasn't changed at all.
</script>

The result will still be the same even if you switch the string property with object.

This is inconsistent behavior. The issue is from how $derived works when destructured since the $props rune's output is also an object and that is also used with a destructuring syntax but this issue doesn't happen with $props. The issue can be fixed if each property is separately $derived but this workaround is tedious and you also can't no longer use the ...rest syntax. perhaps the compiler can look at whether the $derived output is destructured or not and if they are destructured, then treat their dependency like it would with

let number = $derived(mainObject.number); 

This code in playground

Logs

No response

System Info

System:
    OS: Windows 11 10.0.22621
    CPU: (16) x64 12th Gen Intel(R) Core(TM) i7-12650H
    Memory: 7.04 GB / 15.63 GB
  Binaries:
    Node: 18.14.2 - C:\Program Files\nodejs\node.EXE
    npm: 9.7.1 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Chromium (120.0.2210.144)
    Internet Explorer: 11.0.22621.1
  npmPackages:
    svelte: ^5.0.0-next.40 => 5.0.0-next.40

Severity

blocking an upgrade

There is actually a PR open at the moment to fix this: https://github.com/sveltejs/svelte/pull/10302

MotionlessTrain avatar Jan 27 '24 15:01 MotionlessTrain

There is actually a PR open at the moment to fix this: #10302

OMG. Thanks, for letting me know about it. I made this issue (#10007) for the same reason exactly one month ago and hadn't gotten a proper answer for it ever since so I decided to make another issue and this time focus on the main problem. But you answered it with one line. I'm really waiting for this feature since it affects my work a lot. thanks again

Arguably $inspect("derivedObject.string", derivedObject.string); should rerun after 3 seconds (we closed #10302 because it would not give people a way to opt out of the fine-grained update behavior [some may want the coarse-grained behavior, this is really a matter of opinion / how you think about the system, so both should work], has a few rough edge cases would increase the runtime size)

dummdidumm avatar Feb 02 '24 09:02 dummdidumm

Fixed by #10488

dummdidumm avatar Feb 22 '24 11:02 dummdidumm