From 339782f3e09652dac23da16a5685e9a36421ce02 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 13 Mar 2024 15:20:35 +0100 Subject: [PATCH] fix: handle component binding mutation (#10786) * fix: handle component binding mutation https://github.com/sveltejs/svelte/issues/10359#issuecomment-1991885046 * alternative approach to mutating props (#10788) Co-authored-by: Rich Harris --------- Co-authored-by: Rich Harris Co-authored-by: Rich Harris --- .changeset/witty-readers-provide.md | 5 +++ .../phases/3-transform/client/utils.js | 14 ++++---- .../src/internal/client/reactivity/props.js | 6 ++-- .../component-binding-deep2/Widget.svelte | 5 +++ .../component-binding-deep2/_config.js | 32 +++++++++++++++++++ .../component-binding-deep2/main.svelte | 11 +++++++ 6 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 .changeset/witty-readers-provide.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/Widget.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/main.svelte diff --git a/.changeset/witty-readers-provide.md b/.changeset/witty-readers-provide.md new file mode 100644 index 000000000000..60a6bdf009c5 --- /dev/null +++ b/.changeset/witty-readers-provide.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: handle component binding mutation diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index 91a56def4589..d93f8f5b92c8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -470,12 +470,14 @@ export function serialize_set_binding(node, context, fallback, options) { if (binding.kind === 'prop') { return b.call( left, - b.assignment( - node.operator, - /** @type {import('estree').Pattern} */ (visit(node.left)), - value - ), - b.literal(true) + b.sequence([ + b.assignment( + node.operator, + /** @type {import('estree').Pattern} */ (visit(node.left)), + value + ), + b.call(left) + ]) ); } else { return b.call( diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index b3559ae99fed..586f0e64b1b6 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -210,7 +210,7 @@ export function prop(props, key, flags, initial) { if (!immutable) current_value.equals = safe_equals; - return function (/** @type {V} */ value, mutation = false) { + return function (/** @type {V} */ value) { var current = get(current_value); // legacy nonsense — need to ensure the source is invalidated when necessary @@ -226,9 +226,9 @@ export function prop(props, key, flags, initial) { } if (arguments.length > 0) { - if (mutation || (immutable ? value !== current : safe_not_equal(value, current))) { + if (!current_value.equals(value)) { from_child = true; - set(inner_current_value, mutation ? current : value); + set(inner_current_value, value); get(current_value); // force a synchronisation immediately } diff --git a/packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/Widget.svelte b/packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/Widget.svelte new file mode 100644 index 000000000000..c28dfbda901e --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/Widget.svelte @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/_config.js b/packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/_config.js new file mode 100644 index 000000000000..0479b7c5299d --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/_config.js @@ -0,0 +1,32 @@ +import { ok, test } from '../../test'; + +export default test({ + html: ` + +

foo

+ `, + + ssrHtml: ` + +

foo

+ `, + + async test({ assert, component, target, window }) { + const event = new window.MouseEvent('input'); + const input = target.querySelector('input'); + ok(input); + + input.value = 'blah'; + await input.dispatchEvent(event); + await Promise.resolve(); + + assert.deepEqual(component.deep, { name: 'blah' }); + assert.htmlEqual( + target.innerHTML, + ` + +

blah

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/main.svelte b/packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/main.svelte new file mode 100644 index 000000000000..db8cd83b91b3 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/component-binding-deep2/main.svelte @@ -0,0 +1,11 @@ + + + + +

{deep.name}

\ No newline at end of file