mirror of https://github.com/sveltejs/svelte
fix: prevent false positive ownership warning when reassigning state (#11812)
When a proxy is reassigned, we call `$.proxy` again. There are cases where there's a component context set but the reassignment actually happens for variable that is ownerless within shared state or somewhere else. In that case we get false positives right now. The inverse is also true where reassigning can delete owners (because no component context exists) and result in false negatives. The fix is to pass the previous value in to copy over the owners from it. Fixes #11525pull/11835/head
parent
fd942b7e65
commit
bcb319310f
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"svelte": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: prevent buggy ownership warning when reassigning state
|
@ -1,8 +1,8 @@
|
|||||||
<script>
|
<script>
|
||||||
/** @type {{ object: { count: number }}} */
|
let { object = $bindable(), reset } = $props();
|
||||||
let { object = $bindable() } = $props();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button onclick={() => object.count += 1}>
|
<button onclick={() => object.count += 1}>
|
||||||
clicks: {object.count}
|
clicks: {object.count}
|
||||||
</button>
|
</button>
|
||||||
|
<button onclick={reset}>reset</button>
|
||||||
|
@ -1,36 +1,30 @@
|
|||||||
import { flushSync } from 'svelte';
|
import { flushSync } from 'svelte';
|
||||||
import { test } from '../../test';
|
import { test } from '../../test';
|
||||||
|
|
||||||
/** @type {typeof console.trace} */
|
|
||||||
let trace;
|
|
||||||
|
|
||||||
export default test({
|
export default test({
|
||||||
html: `<button>clicks: 0</button>`,
|
html: `<button>clicks: 0</button> <button>reset</button>`,
|
||||||
|
|
||||||
compileOptions: {
|
compileOptions: {
|
||||||
dev: true
|
dev: true
|
||||||
},
|
},
|
||||||
|
|
||||||
before_test: () => {
|
|
||||||
trace = console.trace;
|
|
||||||
console.trace = () => {};
|
|
||||||
},
|
|
||||||
|
|
||||||
after_test: () => {
|
|
||||||
console.trace = trace;
|
|
||||||
},
|
|
||||||
|
|
||||||
test({ assert, target, warnings }) {
|
test({ assert, target, warnings }) {
|
||||||
const btn = target.querySelector('button');
|
const warning =
|
||||||
|
'Counter.svelte mutated a value owned by main.svelte. This is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead';
|
||||||
flushSync(() => {
|
const [btn1, btn2] = target.querySelectorAll('button');
|
||||||
btn?.click();
|
|
||||||
});
|
btn1.click();
|
||||||
|
flushSync();
|
||||||
assert.htmlEqual(target.innerHTML, `<button>clicks: 1</button>`);
|
assert.htmlEqual(target.innerHTML, `<button>clicks: 1</button> <button>reset</button>`);
|
||||||
|
assert.deepEqual(warnings, [warning]);
|
||||||
assert.deepEqual(warnings, [
|
|
||||||
'Counter.svelte mutated a value owned by main.svelte. This is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead'
|
btn2.click();
|
||||||
]);
|
flushSync();
|
||||||
|
assert.htmlEqual(target.innerHTML, `<button>clicks: 0</button> <button>reset</button>`);
|
||||||
|
|
||||||
|
btn1.click();
|
||||||
|
flushSync();
|
||||||
|
assert.htmlEqual(target.innerHTML, `<button>clicks: 1</button> <button>reset</button>`);
|
||||||
|
assert.deepEqual(warnings, [warning, warning]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Counter from './Counter.svelte';
|
import Counter from './Counter.svelte';
|
||||||
|
|
||||||
const object = $state({ count: 0 });
|
let object = $state({ count: 0 });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Counter {object} />
|
<Counter {object} reset={() => object = {count: 0}} />
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
compileOptions: {
|
||||||
|
dev: true
|
||||||
|
},
|
||||||
|
|
||||||
|
async test({ assert, warnings }) {
|
||||||
|
assert.deepEqual(warnings, []);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,18 @@
|
|||||||
|
<script context="module">
|
||||||
|
let toast1 = $state();
|
||||||
|
let toast2 = $state({});
|
||||||
|
|
||||||
|
export async function show_toast() {
|
||||||
|
toast1 = {
|
||||||
|
message: 'foo',
|
||||||
|
show: true
|
||||||
|
};
|
||||||
|
toast1.show = false;
|
||||||
|
|
||||||
|
toast2 = {
|
||||||
|
message: 'foo',
|
||||||
|
show: true
|
||||||
|
};
|
||||||
|
toast2.show = false;
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,5 @@
|
|||||||
|
<script>
|
||||||
|
import { show_toast } from "./child.svelte";
|
||||||
|
|
||||||
|
show_toast();
|
||||||
|
</script>
|
Loading…
Reference in new issue