mirror of https://github.com/sveltejs/svelte
prevents infinite loops when mutation happens inside an effect came up in #9639pull/9685/head
parent
a31b2e1b8e
commit
82f3be2a66
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: untrack reads of mutated object
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import { flushSync } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
// Test ensures that reading the state that's mutated is done in a manner
|
||||||
|
// so that the read is untracked so it doesn't trigger infinite loops
|
||||||
|
export default test({
|
||||||
|
html: `
|
||||||
|
<input>
|
||||||
|
<input>
|
||||||
|
<p>text: foo</p>
|
||||||
|
<p>wrapped.contents: foo</p>
|
||||||
|
`,
|
||||||
|
skip_if_ssr: true,
|
||||||
|
|
||||||
|
test({ assert, target }) {
|
||||||
|
const [input1, input2] = target.querySelectorAll('input');
|
||||||
|
|
||||||
|
input1.value = 'bar';
|
||||||
|
flushSync(() => input1.dispatchEvent(new Event('input')));
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<input>
|
||||||
|
<input>
|
||||||
|
<p>text: bar</p>
|
||||||
|
<p>wrapped.contents: bar</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
input2.value = 'baz';
|
||||||
|
flushSync(() => input2.dispatchEvent(new Event('input')));
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<input>
|
||||||
|
<input>
|
||||||
|
<p>text: bar</p>
|
||||||
|
<p>wrapped.contents: baz</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
input1.value = 'foo';
|
||||||
|
flushSync(() => input1.dispatchEvent(new Event('input')));
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<input>
|
||||||
|
<input>
|
||||||
|
<p>text: foo</p>
|
||||||
|
<p>wrapped.contents: foo</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
<script>
|
||||||
|
let text = $state('foo');
|
||||||
|
let wrapped = $state({});
|
||||||
|
$effect(() => { wrapped.contents = text; })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input bind:value={text} />
|
||||||
|
<input bind:value={wrapped.contents} />
|
||||||
|
<p>text: {text}</p>
|
||||||
|
<p>wrapped.contents: {wrapped.contents}</p>
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import { flushSync } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
// Test ensures that reading the state that's mutated is done in a manner
|
||||||
|
// so that the read is untracked so it doesn't trigger infinite loops
|
||||||
|
export default test({
|
||||||
|
html: `
|
||||||
|
<input>
|
||||||
|
<input>
|
||||||
|
<p>text: foo</p>
|
||||||
|
<p>wrapped.contents: foo</p>
|
||||||
|
`,
|
||||||
|
skip_if_ssr: true,
|
||||||
|
|
||||||
|
test({ assert, target }) {
|
||||||
|
const [input1, input2] = target.querySelectorAll('input');
|
||||||
|
|
||||||
|
input1.value = 'bar';
|
||||||
|
flushSync(() => input1.dispatchEvent(new Event('input')));
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<input>
|
||||||
|
<input>
|
||||||
|
<p>text: bar</p>
|
||||||
|
<p>wrapped.contents: bar</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
input2.value = 'baz';
|
||||||
|
flushSync(() => input2.dispatchEvent(new Event('input')));
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<input>
|
||||||
|
<input>
|
||||||
|
<p>text: bar</p>
|
||||||
|
<p>wrapped.contents: baz</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
input1.value = 'foo';
|
||||||
|
flushSync(() => input1.dispatchEvent(new Event('input')));
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<input>
|
||||||
|
<input>
|
||||||
|
<p>text: foo</p>
|
||||||
|
<p>wrapped.contents: foo</p>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<script>
|
||||||
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
|
let text = writable('foo');
|
||||||
|
let wrapped = writable({});
|
||||||
|
$effect(() => { $wrapped.contents = $text; })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input bind:value={$text} />
|
||||||
|
<input bind:value={$wrapped.contents} />
|
||||||
|
<p>text: {$text}</p>
|
||||||
|
<p>wrapped.contents: {$wrapped.contents}</p>
|
||||||
Loading…
Reference in new issue