From 5446ba81e4c4b47fe1859f0e05f9c09f04f65864 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 6 Feb 2024 15:51:35 +0000 Subject: [PATCH 1/4] fix: only update lazy properties that have actually changed --- packages/svelte/src/internal/client/each.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/each.js b/packages/svelte/src/internal/client/each.js index 032edad201b0..a2b3ee0f9683 100644 --- a/packages/svelte/src/internal/client/each.js +++ b/packages/svelte/src/internal/client/each.js @@ -789,7 +789,16 @@ function update_each_item_block(block, item, index, type) { if ((type & EACH_ITEM_REACTIVE) !== 0) { set_signal_value(block.v, item); } else if (is_lazy_property(block.v)) { - block.v.o[block.v.p] = item; + // If we have lazy properties, it means that an array was used that has been + // proxied. Given this, we need to re-sync the old array by mutating the backing + // value to be the latest value to ensure the UI updates correctly. TODO: maybe + // we should bypass any internal mutation checks for this? + const o = block.v.o; + const p = block.v.p; + const prev = o[p]; + if (prev !== item) { + o[p] = item; + } } const transitions = block.s; const index_is_reactive = (type & EACH_INDEX_REACTIVE) !== 0; From d7c2d502744f09dc207da272b0e9778e48993dc5 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 6 Feb 2024 15:52:47 +0000 Subject: [PATCH 2/4] code golf --- packages/svelte/src/internal/client/each.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/svelte/src/internal/client/each.js b/packages/svelte/src/internal/client/each.js index a2b3ee0f9683..8485f519582e 100644 --- a/packages/svelte/src/internal/client/each.js +++ b/packages/svelte/src/internal/client/each.js @@ -786,15 +786,16 @@ export function get_first_element(block) { * @returns {void} */ function update_each_item_block(block, item, index, type) { + const block_v = block.v; if ((type & EACH_ITEM_REACTIVE) !== 0) { - set_signal_value(block.v, item); - } else if (is_lazy_property(block.v)) { + set_signal_value(block_v, item); + } else if (is_lazy_property(block_v)) { // If we have lazy properties, it means that an array was used that has been // proxied. Given this, we need to re-sync the old array by mutating the backing // value to be the latest value to ensure the UI updates correctly. TODO: maybe // we should bypass any internal mutation checks for this? - const o = block.v.o; - const p = block.v.p; + const o = block_v.o; + const p = block_v.p; const prev = o[p]; if (prev !== item) { o[p] = item; From 06b308f8925ece2eada2b834f883c3758e968eb4 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 6 Feb 2024 15:53:04 +0000 Subject: [PATCH 3/4] changeset --- .changeset/rare-worms-hunt.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rare-worms-hunt.md diff --git a/.changeset/rare-worms-hunt.md b/.changeset/rare-worms-hunt.md new file mode 100644 index 000000000000..e0963bd6af21 --- /dev/null +++ b/.changeset/rare-worms-hunt.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: only update lazy properties that have actually changed From c9d8bccffe1db2e86232ad7ced4cabb79b709407 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 6 Feb 2024 16:15:22 +0000 Subject: [PATCH 4/4] Add test --- .../samples/readonly-state-push/Child.svelte | 7 +++++++ .../samples/readonly-state-push/_config.js | 14 ++++++++++++++ .../samples/readonly-state-push/main.svelte | 13 +++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 packages/svelte/tests/runtime-runes/samples/readonly-state-push/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/readonly-state-push/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/readonly-state-push/main.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/readonly-state-push/Child.svelte b/packages/svelte/tests/runtime-runes/samples/readonly-state-push/Child.svelte new file mode 100644 index 000000000000..dc79fc19bf58 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/readonly-state-push/Child.svelte @@ -0,0 +1,7 @@ + + +{#each array as number} +

{number}

+{/each} diff --git a/packages/svelte/tests/runtime-runes/samples/readonly-state-push/_config.js b/packages/svelte/tests/runtime-runes/samples/readonly-state-push/_config.js new file mode 100644 index 000000000000..3d3895312722 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/readonly-state-push/_config.js @@ -0,0 +1,14 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [b1] = target.querySelectorAll('button'); + b1.click(); + await Promise.resolve(); + + assert.htmlEqual( + target.innerHTML, + '

1

2

3

4

0

' + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/readonly-state-push/main.svelte b/packages/svelte/tests/runtime-runes/samples/readonly-state-push/main.svelte new file mode 100644 index 000000000000..51c0940b9793 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/readonly-state-push/main.svelte @@ -0,0 +1,13 @@ + + + + +