mirror of https://github.com/sveltejs/svelte
fix: better binding interop between runes/non-runes components (#12123)
* Ensure binding from legacy component passed to runes component updates correctly. This is done by also using the `prop(..)` variant for a property if it's mutated in runes mode, and then figuring out at runtime whether or not the parent should be notified or not fixes #12032 * fix adjacent bug around wrong value getting return upon mutation * deduplicate * changeset * simplify * move comment * rename --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>pull/12125/head
parent
6c66680919
commit
afe84450c6
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"svelte": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: better binding interop between runes/non-runes components
|
@ -0,0 +1,8 @@
|
|||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
mode: ['client'],
|
||||||
|
test({ assert, logs }) {
|
||||||
|
assert.deepEqual(logs, [true]);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,4 @@
|
|||||||
|
<script>
|
||||||
|
export let a = {};
|
||||||
|
console.log((a.b = true));
|
||||||
|
</script>
|
@ -0,0 +1,9 @@
|
|||||||
|
<script>
|
||||||
|
let { object = $bindable(), primitive = $bindable() } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if primitive}
|
||||||
|
<button onclick={() => (primitive = 'bar')}>{primitive}</button>
|
||||||
|
{:else}
|
||||||
|
<button onclick={() => (object.value = 'bar')}>{object.value}</button>
|
||||||
|
{/if}
|
@ -0,0 +1,9 @@
|
|||||||
|
<script>
|
||||||
|
let { object = $bindable({}), primitive = $bindable('') } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if primitive}
|
||||||
|
<button onclick={() => (primitive = 'bar')}>{primitive}</button>
|
||||||
|
{:else}
|
||||||
|
<button onclick={() => (object.value = 'bar')}>{object.value}</button>
|
||||||
|
{/if}
|
@ -0,0 +1,24 @@
|
|||||||
|
<svelte:options runes={false} />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Component1 from './Component1.svelte';
|
||||||
|
import Component2 from './Component2.svelte';
|
||||||
|
|
||||||
|
let object1 = { value: 'foo' };
|
||||||
|
let object2 = { value: 'foo' };
|
||||||
|
|
||||||
|
let primitive1 = 'foo';
|
||||||
|
let primitive2 = 'foo';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{object1.value}
|
||||||
|
<Component1 bind:object={object1} />
|
||||||
|
|
||||||
|
{object2.value}
|
||||||
|
<Component2 bind:object={object2} />
|
||||||
|
|
||||||
|
{primitive1}
|
||||||
|
<Component1 bind:primitive={primitive1} />
|
||||||
|
|
||||||
|
{primitive2}
|
||||||
|
<Component2 bind:primitive={primitive2} />
|
@ -0,0 +1,39 @@
|
|||||||
|
<script>
|
||||||
|
import Component1 from './Component1.svelte';
|
||||||
|
import Component2 from './Component2.svelte';
|
||||||
|
|
||||||
|
let object1 = $state({ value: 'foo' });
|
||||||
|
let object2 = $state({ value: 'foo' });
|
||||||
|
|
||||||
|
class Frozen {
|
||||||
|
constructor(value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let object3 = $state(new Frozen('foo'));
|
||||||
|
let object4 = $state(new Frozen('foo'));
|
||||||
|
|
||||||
|
let primitive1 = $state('foo');
|
||||||
|
let primitive2 = $state('foo');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{object1.value}
|
||||||
|
<Component1 bind:object={object1} />
|
||||||
|
|
||||||
|
{object2.value}
|
||||||
|
<Component2 bind:object={object2} />
|
||||||
|
|
||||||
|
<!-- force them into a different render effect so they don't coincidently update with the others -->
|
||||||
|
{#if true}
|
||||||
|
{object3.value}
|
||||||
|
<Component1 bind:object={object3} />
|
||||||
|
|
||||||
|
{object4.value}
|
||||||
|
<Component2 bind:object={object4} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{primitive1}
|
||||||
|
<Component1 bind:primitive={primitive1} />
|
||||||
|
|
||||||
|
{primitive2}
|
||||||
|
<Component2 bind:primitive={primitive2} />
|
@ -0,0 +1,24 @@
|
|||||||
|
import { flushSync } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
mode: ['client'],
|
||||||
|
async test({ assert, target }) {
|
||||||
|
const buttons = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
for (const button of buttons) {
|
||||||
|
await button.click();
|
||||||
|
flushSync();
|
||||||
|
}
|
||||||
|
flushSync();
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
bar <button>bar</button> bar <button>bar</button> bar <button>bar</button> bar <button>bar</button>
|
||||||
|
<hr>
|
||||||
|
bar <button>bar</button> bar <button>bar</button> foo <button>foo</button> foo <button>foo</button> bar <button>bar</button> bar <button>bar</button>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,10 @@
|
|||||||
|
<script>
|
||||||
|
import Legacy from './Legacy.svelte';
|
||||||
|
import Runes from './Runes.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Legacy />
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<Runes />
|
Loading…
Reference in new issue