Skip to content

Commit 68a2263

Browse files
authored
fix: ensure local prop value is read during teardown (#13611)
* fix: ensure local prop value is read during teardown * add test * cleanup * less overhead
1 parent 829be3d commit 68a2263

File tree

5 files changed

+60
-3
lines changed

5 files changed

+60
-3
lines changed

.changeset/clean-sloths-nail.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 local prop value is read during teardown

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @import { Source } from './types.js' */
1+
/** @import { Derived, Source } from './types.js' */
22
import { DEV } from 'esm-env';
33
import {
44
PROPS_IS_BINDABLE,
@@ -12,6 +12,7 @@ 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,
1516
get,
1617
is_signals_recorded,
1718
set_active_effect,
@@ -20,7 +21,7 @@ import {
2021
} from '../runtime.js';
2122
import { safe_equals } from './equality.js';
2223
import * as e from '../errors.js';
23-
import { BRANCH_EFFECT, LEGACY_DERIVED_PROP, ROOT_EFFECT } from '../constants.js';
24+
import { BRANCH_EFFECT, DESTROYED, LEGACY_DERIVED_PROP, ROOT_EFFECT } from '../constants.js';
2425
import { proxy } from '../proxy.js';
2526

2627
/**
@@ -348,12 +349,17 @@ export function prop(props, key, flags, fallback) {
348349
// The derived returns the current value. The underlying mutable
349350
// source is written to from various places to persist this value.
350351
var inner_current_value = mutable_source(prop_value);
352+
351353
var current_value = with_parent_branch(() =>
352354
derived(() => {
353355
var parent_value = getter();
354356
var child_value = get(inner_current_value);
357+
var current_derived = /** @type {Derived} */ (active_reaction);
355358

356-
if (from_child) {
359+
// If the getter from the parent returns undefined, switch
360+
// to using the local value from inner_current_value instead,
361+
// as the parent value might have been torn down
362+
if (from_child || (parent_value === undefined && (current_derived.f & DESTROYED) !== 0)) {
357363
from_child = false;
358364
was_from_child = true;
359365
return child_value;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script>
2+
let { x } = $props();
3+
4+
$effect(() => {
5+
console.log('init')
6+
7+
x = () => {
8+
console.log('teardown')
9+
}
10+
11+
return () => {
12+
x();
13+
}
14+
})
15+
</script>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
async test({ assert, logs, target }) {
6+
const [btn1] = target.querySelectorAll('button');
7+
8+
btn1?.click();
9+
flushSync();
10+
11+
btn1?.click();
12+
flushSync();
13+
14+
btn1?.click();
15+
flushSync();
16+
17+
assert.deepEqual(logs, ['init', 'teardown', 'init', 'teardown']);
18+
}
19+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
import Component from "./Component.svelte";
3+
4+
let toggle = $state(true);
5+
</script>
6+
7+
<button onclick={() => toggle = !toggle}>toggle</button>
8+
9+
{#if toggle}
10+
<Component />
11+
{/if}
12+

0 commit comments

Comments
 (0)