fix: prevent infinite loops stemming from invalidation method (#9811)

* fix: prevent infinite loops stemming from invalidation method

The logic was flawed: the captured signals where always added to the previous captured no matter what, which meant a) memory leak b) that when another one runs afterwards, it will falsely contain the signals from the previous run
fixes #9788

* fix lint
pull/9821/head
Simon H 1 year ago committed by GitHub
parent edc569e73b
commit 074615d7fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: prevent infinite loops stemming from invalidation method

@ -863,28 +863,28 @@ export function set_sync(signal, value) {
* Invokes a function and captures all signals that are read during the invocation, * Invokes a function and captures all signals that are read during the invocation,
* then invalidates them. * then invalidates them.
* @param {() => any} fn * @param {() => any} fn
* @returns {Set<import('./types.js').Signal>}
*/ */
export function invalidate_inner_signals(fn) { export function invalidate_inner_signals(fn) {
const previous_is_signals_recorded = is_signals_recorded; var previous_is_signals_recorded = is_signals_recorded;
const previous_captured_signals = captured_signals; var previous_captured_signals = captured_signals;
is_signals_recorded = true; is_signals_recorded = true;
captured_signals = new Set(); captured_signals = new Set();
var captured = captured_signals;
var signal;
try { try {
untrack(fn); untrack(fn);
} finally { } finally {
is_signals_recorded = previous_is_signals_recorded; is_signals_recorded = previous_is_signals_recorded;
let signal; if (is_signals_recorded) {
for (signal of captured_signals) { for (signal of captured_signals) {
previous_captured_signals.add(signal); previous_captured_signals.add(signal);
}
} }
captured_signals = previous_captured_signals; captured_signals = previous_captured_signals;
} }
let signal; for (signal of captured) {
for (signal of captured_signals) {
mutate(signal, null /* doesnt matter */); mutate(signal, null /* doesnt matter */);
} }
return captured_signals;
} }
/** /**

@ -0,0 +1,32 @@
import { flushSync } from 'svelte';
import { ok, test } from '../../test';
export default test({
html: `
<select>
<option value="a">A</option>
<option value="b">B</option>
</select>
selected: a
`,
test({ assert, target }) {
const select = target.querySelector('select');
ok(select);
const event = new window.Event('change');
select.value = 'b';
select.dispatchEvent(event);
flushSync();
assert.htmlEqual(
target.innerHTML,
`
<select>
<option value="a">A</option>
<option value="b">B</option>
</select>
selected: b
`
);
}
});

@ -0,0 +1,11 @@
<script>
let entries = [{selected: 'a' }]
</script>
{#each entries as entry}
<select bind:value={entry.selected}>
<option value='a'>A</option>
<option value='b'>B</option>
</select>
selected: {entry.selected}
{/each}
Loading…
Cancel
Save