fix: handle duplicate signal dependencies gracefully (#12261)

* fix: further avoid duplicate signal dependencies

visitor

another approach

tune

tune

* add test

* clean up test

* early return to reduce indentation, use var over let/const

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/12268/head
Dominic Gannaway 6 months ago committed by GitHub
parent ccacfc7955
commit d2530ed4a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: handle duplicate signal dependencies gracefully

@ -417,16 +417,21 @@ function remove_reaction(signal, dependency) {
* @returns {void} * @returns {void}
*/ */
export function remove_reactions(signal, start_index) { export function remove_reactions(signal, start_index) {
const dependencies = signal.deps; var dependencies = signal.deps;
if (dependencies !== null) { if (dependencies === null) return;
const active_dependencies = start_index === 0 ? null : dependencies.slice(0, start_index);
let i; var active_dependencies = start_index === 0 ? null : dependencies.slice(0, start_index);
for (i = start_index; i < dependencies.length; i++) { var seen = new Set();
const dependency = dependencies[i];
// Avoid removing a reaction if we know that it is active (start_index will not be 0) for (var i = start_index; i < dependencies.length; i++) {
if (active_dependencies === null || !active_dependencies.includes(dependency)) { var dependency = dependencies[i];
remove_reaction(signal, dependency);
} if (seen.has(dependency)) continue;
seen.add(dependency);
// Avoid removing a reaction if we know that it is active (start_index will not be 0)
if (active_dependencies === null || !active_dependencies.includes(dependency)) {
remove_reaction(signal, dependency);
} }
} }
} }
@ -774,10 +779,7 @@ export function get(signal) {
) { ) {
if (current_dependencies === null) { if (current_dependencies === null) {
current_dependencies = [signal]; current_dependencies = [signal];
} else if ( } else if (current_dependencies[current_dependencies.length - 1] !== signal) {
current_dependencies[current_dependencies.length - 1] !== signal &&
!current_dependencies.includes(signal)
) {
current_dependencies.push(signal); current_dependencies.push(signal);
} }
} }

@ -0,0 +1,37 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
let [btn1, btn2] = target.querySelectorAll('button');
flushSync(() => btn1?.click());
assert.htmlEqual(
target.innerHTML,
`
<button>toggle a</button>
<button>toggle b</button>
false/true/true
`
);
flushSync(() => btn2?.click());
assert.htmlEqual(
target.innerHTML,
`
<button>toggle a</button>
<button>toggle b</button>
`
);
flushSync(() => btn2?.click());
assert.htmlEqual(
target.innerHTML,
`
<button>toggle a</button>
<button>toggle b</button>
false/true/true
`
);
}
});

@ -0,0 +1,13 @@
<script>
let a = $state(true);
let b = $state({ c: true });
const x = $derived(b);
</script>
<button onclick={() => (a = !a)}>toggle a</button>
<button onclick={() => (b = b ? null : { c: true })}>toggle b</button>
{#if x}
{a}/{x.c}/{x.c}
{/if}
Loading…
Cancel
Save