From 863ba1557a318f0d85664812d4464bc64086d4f4 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 13 Nov 2024 03:24:56 -0500 Subject: [PATCH] fix: use strict equality for key block comparisons in runes mode (#14285) fixes #14283 --- .changeset/hot-frogs-melt.md | 5 +++++ .../svelte/src/internal/client/dom/blocks/key.js | 7 +++++-- .../src/internal/client/reactivity/equality.js | 9 +++++++++ .../samples/key-unchanged-value/_config.js | 13 +++++++++++++ .../samples/key-unchanged-value/main.svelte | 10 ++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .changeset/hot-frogs-melt.md create mode 100644 packages/svelte/tests/runtime-runes/samples/key-unchanged-value/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/key-unchanged-value/main.svelte diff --git a/.changeset/hot-frogs-melt.md b/.changeset/hot-frogs-melt.md new file mode 100644 index 000000000000..95b3e0b4b425 --- /dev/null +++ b/.changeset/hot-frogs-melt.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: use strict equality for key block comparisons in runes mode diff --git a/packages/svelte/src/internal/client/dom/blocks/key.js b/packages/svelte/src/internal/client/dom/blocks/key.js index 04b8ab403f00..4a8b7b94fcc8 100644 --- a/packages/svelte/src/internal/client/dom/blocks/key.js +++ b/packages/svelte/src/internal/client/dom/blocks/key.js @@ -1,7 +1,8 @@ /** @import { Effect, TemplateNode } from '#client' */ import { UNINITIALIZED } from '../../../../constants.js'; import { block, branch, pause_effect } from '../../reactivity/effects.js'; -import { safe_not_equal } from '../../reactivity/equality.js'; +import { not_equal, safe_not_equal } from '../../reactivity/equality.js'; +import { is_runes } from '../../runtime.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; /** @@ -24,8 +25,10 @@ export function key_block(node, get_key, render_fn) { /** @type {Effect} */ var effect; + var changed = is_runes() ? not_equal : safe_not_equal; + block(() => { - if (safe_not_equal(key, (key = get_key()))) { + if (changed(key, (key = get_key()))) { if (effect) { pause_effect(effect); } diff --git a/packages/svelte/src/internal/client/reactivity/equality.js b/packages/svelte/src/internal/client/reactivity/equality.js index 751c8720f9bd..37a9994ab8cc 100644 --- a/packages/svelte/src/internal/client/reactivity/equality.js +++ b/packages/svelte/src/internal/client/reactivity/equality.js @@ -15,6 +15,15 @@ export function safe_not_equal(a, b) { : a !== b || (a !== null && typeof a === 'object') || typeof a === 'function'; } +/** + * @param {unknown} a + * @param {unknown} b + * @returns {boolean} + */ +export function not_equal(a, b) { + return a !== b; +} + /** @type {Equals} */ export function safe_equals(value) { return !safe_not_equal(value, this.v); diff --git a/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/_config.js b/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/_config.js new file mode 100644 index 000000000000..0c56330a3ef8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target, logs }) { + assert.deepEqual(logs, ['rendering']); + + const btn = target.querySelector('button'); + flushSync(() => btn?.click()); + + assert.deepEqual(logs, ['rendering']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/main.svelte b/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/main.svelte new file mode 100644 index 000000000000..47ecd20c10fb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/key-unchanged-value/main.svelte @@ -0,0 +1,10 @@ + + + + +{#key outer.inner} + {console.log('rendering')} +{/key}