Skip to content

Commit cdce350

Browse files
committed
better approach
1 parent 60d5da1 commit cdce350

File tree

6 files changed

+49
-27
lines changed

6 files changed

+49
-27
lines changed

.changeset/khaki-carrots-mix.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'svelte': patch
33
---
44

5-
fix: ensure derived props cache last value when destroyed
5+
fix: ensure child effects are destroyed before their deriveds

packages/svelte/src/internal/client/reactivity/effects.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,8 +437,8 @@ export function destroy_effect(effect, remove_dom = true) {
437437
removed = true;
438438
}
439439

440-
destroy_effect_deriveds(effect);
441440
destroy_effect_children(effect, remove_dom && !removed);
441+
destroy_effect_deriveds(effect);
442442
remove_reactions(effect, 0);
443443
set_signal_status(effect, DESTROYED);
444444

packages/svelte/src/internal/client/reactivity/props.js

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -313,42 +313,25 @@ export function prop(props, key, flags, fallback) {
313313

314314
/** @type {() => V} */
315315
var getter;
316-
var legacy_parent = props.$$legacy;
317-
// Svelte 4 did not trigger updates when a primitive value was updated to the same value.
318-
// Replicate that behavior through using a derived
319-
var derived_getter = with_parent_branch(() => {
320-
return ((!runes && immutable) || (runes && !legacy_parent) ? derived : derived_safe_equal)(
321-
() => /** @type {V} */ (props[key])
322-
);
323-
});
324-
// Connect the derived getter to the parent branch effect
325-
get(derived_getter);
326-
/** @type {V} */
327-
var last_value;
328-
329316
if (runes) {
330317
getter = () => {
331-
// If the derived has been destroyed, use the last value we have
332-
if ((derived_getter.f & DESTROYED) !== 0) {
333-
return last_value;
334-
}
335-
var value = get(derived_getter);
336-
if (value === undefined) return (last_value = get_fallback());
318+
var value = /** @type {V} */ (props[key]);
319+
if (value === undefined) return get_fallback();
337320
fallback_dirty = true;
338321
fallback_used = false;
339-
last_value = value;
340322
return value;
341323
};
342324
} else {
325+
// Svelte 4 did not trigger updates when a primitive value was updated to the same value.
326+
// Replicate that behavior through using a derived
327+
var derived_getter = with_parent_branch(() =>
328+
(immutable ? derived : derived_safe_equal)(() => /** @type {V} */ (props[key]))
329+
);
343330
derived_getter.f |= LEGACY_DERIVED_PROP;
344331
getter = () => {
345-
// If the derived has been destroyed, use the last value we have
346-
if ((derived_getter.f & DESTROYED) !== 0) {
347-
return last_value;
348-
}
349332
var value = get(derived_getter);
350333
if (value !== undefined) fallback_value = /** @type {V} */ (undefined);
351-
return (last_value = value === undefined ? fallback_value : value);
334+
return value === undefined ? fallback_value : value;
352335
};
353336
}
354337

@@ -360,6 +343,7 @@ export function prop(props, key, flags, fallback) {
360343
// intermediate mode — prop is written to, but the parent component had
361344
// `bind:foo` which means we can just call `$$props.foo = value` directly
362345
if (setter) {
346+
var legacy_parent = props.$$legacy;
363347
return function (/** @type {any} */ value, /** @type {boolean} */ mutation) {
364348
if (arguments.length > 0) {
365349
// We don't want to notify if the value was mutated and the parent is in runes mode.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
import { onDestroy } from 'svelte';
3+
4+
const { data = 123 } = $props();
5+
6+
onDestroy(() => {
7+
data;
8+
});
9+
</script>
10+
11+
{data ? '' : null}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
test({ assert, logs, target }) {
6+
target.querySelector('button')?.click();
7+
flushSync();
8+
assert.deepEqual(logs, ['should fire once']);
9+
}
10+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script>
2+
import Child from './Child.svelte';
3+
4+
let active = $state(true);
5+
let data = $state({ example: 'This is some example data' });
6+
7+
function log(data) {
8+
console.log('should fire once');
9+
return data;
10+
}
11+
</script>
12+
13+
<button onclick={() => (active = false)}>Hide</button>
14+
15+
{#if active}
16+
<Child data={log(data)} />
17+
{/if}

0 commit comments

Comments
 (0)