Skip to content

Commit bce1c48

Browse files
authored
fix: ensure child effects are destroyed before their deriveds (#14043)
* fix: ensure legacy props cache last value when destroyed * fix runes * better approach * better approach * kill code * lint
1 parent f519b3d commit bce1c48

File tree

10 files changed

+86
-11
lines changed

10 files changed

+86
-11
lines changed

.changeset/khaki-carrots-mix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
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: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @import { Derived, Source } from './types.js' */
1+
/** @import { Source } from './types.js' */
22
import { DEV } from 'esm-env';
33
import {
44
PROPS_IS_BINDABLE,
@@ -12,7 +12,6 @@ import { mutable_source, set, source } from './sources.js';
1212
import { derived, derived_safe_equal } from './deriveds.js';
1313
import {
1414
active_effect,
15-
active_reaction,
1615
get,
1716
is_signals_recorded,
1817
set_active_effect,
@@ -21,7 +20,7 @@ import {
2120
} from '../runtime.js';
2221
import { safe_equals } from './equality.js';
2322
import * as e from '../errors.js';
24-
import { BRANCH_EFFECT, DESTROYED, LEGACY_DERIVED_PROP, ROOT_EFFECT } from '../constants.js';
23+
import { BRANCH_EFFECT, LEGACY_DERIVED_PROP, ROOT_EFFECT } from '../constants.js';
2524
import { proxy } from '../proxy.js';
2625
import { capture_store_binding } from './store.js';
2726

@@ -369,17 +368,12 @@ export function prop(props, key, flags, fallback) {
369368
// The derived returns the current value. The underlying mutable
370369
// source is written to from various places to persist this value.
371370
var inner_current_value = mutable_source(prop_value);
372-
373371
var current_value = with_parent_branch(() =>
374372
derived(() => {
375373
var parent_value = getter();
376374
var child_value = get(inner_current_value);
377-
var current_derived = /** @type {Derived} */ (active_reaction);
378375

379-
// If the getter from the parent returns undefined, switch
380-
// to using the local value from inner_current_value instead,
381-
// as the parent value might have been torn down
382-
if (from_child || (parent_value === undefined && (current_derived.f & DESTROYED) !== 0)) {
376+
if (from_child) {
383377
from_child = false;
384378
was_from_child = true;
385379
return child_value;

packages/svelte/src/internal/client/runtime.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,12 +432,12 @@ export function update_effect(effect) {
432432
}
433433

434434
try {
435-
destroy_effect_deriveds(effect);
436435
if ((flags & BLOCK_EFFECT) !== 0) {
437436
destroy_block_effect_children(effect);
438437
} else {
439438
destroy_effect_children(effect);
440439
}
440+
destroy_effect_deriveds(effect);
441441

442442
execute_effect_teardown(effect);
443443
var teardown = update_reaction(effect);
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+
export let data;
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 = true;
5+
let data = { 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 on:click={() => active = false}>Hide</button>
14+
15+
{#if active}
16+
<Child data={log(data)} />
17+
{/if}
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)