Skip to content

Change detection is overly aggressive where destructuring is used #6599

@Rich-Harris

Description

@Rich-Harris

Describe the bug

In a reactive declaration like this...

$: a = b(c);

...Svelte correctly wraps the assignment to a in a call to $$invalidate. b and c are treated as dependents (assuming they are themselves reactive), resulting in the following code if c is reactive:

$$self.$$set = $$props => {
  if ('c' in $$props) $$invalidate(1, c = $$props.c);
};

$$self.$$.update = () => {
  if ($$self.$$.dirty & /*c*/ 2) {
    $: $$invalidate(0, a = b(c));
  }
};

But if the assignment is destructured (and there are two or more destructured names)...

$: ({ x, y } = b(c));

...something rather odd happens:

$$self.$$set = $$props => {
  if ('b' in $$props) $$invalidate(2, b = $$props.b);
  if ('c' in $$props) $$invalidate(3, c = $$props.c);
};

$$self.$$.update = () => {
  if ($$self.$$.dirty & /*b, c*/ 12) {
    $: $$invalidate(1, { x, y } = b(c), x, (($$invalidate(0, y), $$invalidate(2, b)), $$invalidate(3, c)));
  }
};

In this case x and y are correctly invalidated, but b and c are as well.

It's easy enough to work around — just don't use destructuring — but it's also a bit of a bad bug. In our case it caused a major performance headache as something intended to run rarely was instead attempting to run at 60fps (which in turn made it more like 10fps).

Reproduction

The simplest repro I've come up with is this one. This more involved one shows a reactive statement re-running unnecessarily as a result of the bug.

Logs

No response

System Info

System:
    OS: macOS 10.15.7
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
    Memory: 8.12 GB / 64.00 GB
    Shell: 5.7.1 - /bin/zsh
  Binaries:
    Node: 16.5.0 - ~/.nvm/versions/node/v16.5.0/bin/node
    Yarn: 1.22.5 - /usr/local/bin/yarn
    npm: 7.19.1 - ~/.nvm/versions/node/v16.5.0/bin/npm
  Browsers:
    Chrome: 92.0.4515.107
    Firefox: 90.0.1
    Safari: 13.1.3
  npmPackages:
    svelte: ^3.38.2 => 3.38.2 (but note that it happens in 3.41.0, per the REPL)

Severity

annoyance

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions