diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js
index 4d09d9293f..6c21717852 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js
@@ -42,6 +42,9 @@ export function CallExpression(node, context) {
e.bindable_invalid_location(node);
}
+ // We need context in case the bound prop is stale
+ context.state.analysis.needs_context = true;
+
break;
case '$host':
diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js
index 6312360a0d..90e1578eef 100644
--- a/packages/svelte/src/internal/client/constants.js
+++ b/packages/svelte/src/internal/client/constants.js
@@ -24,6 +24,7 @@ export const EFFECT_HAS_DERIVED = 1 << 20;
export const STATE_SYMBOL = Symbol('$state');
export const STATE_SYMBOL_METADATA = Symbol('$state metadata');
export const LEGACY_PROPS = Symbol('legacy props');
+export const TEARDOWN_PROPS = Symbol('teardown props');
export const LOADING_ATTR_SYMBOL = Symbol('');
export const CTX_CONTAINS_TEARDOWN = 1 << 1;
diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js
index 2457a9fa9d..b2c4691fa9 100644
--- a/packages/svelte/src/internal/client/context.js
+++ b/packages/svelte/src/internal/client/context.js
@@ -13,7 +13,7 @@ import {
} from './runtime.js';
import { effect, teardown } from './reactivity/effects.js';
import { legacy_mode_flag } from '../flags/index.js';
-import { CTX_CONTAINS_TEARDOWN, CTX_DESTROYED } from './constants.js';
+import { CTX_CONTAINS_TEARDOWN, CTX_DESTROYED, TEARDOWN_PROPS } from './constants.js';
import { define_property } from '../shared/utils.js';
/** @type {ComponentContext | null} */
@@ -144,6 +144,10 @@ export function push(props, runes = false, fn) {
ctx.f ^= CTX_DESTROYED;
var teardown_props = ctx.tp;
+ if (TEARDOWN_PROPS in props) {
+ props[TEARDOWN_PROPS] = teardown_props;
+ return;
+ }
// Apply the latest known props before teardown over existing props
for (var key in teardown_props) {
define_property(props, key, {
diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js
index a105d83d48..532ea82eb1 100644
--- a/packages/svelte/src/internal/client/reactivity/props.js
+++ b/packages/svelte/src/internal/client/reactivity/props.js
@@ -13,7 +13,13 @@ import { derived, derived_safe_equal } from './deriveds.js';
import { get, captured_signals, untrack } from '../runtime.js';
import { safe_equals } from './equality.js';
import * as e from '../errors.js';
-import { CTX_DESTROYED, LEGACY_DERIVED_PROP, LEGACY_PROPS, STATE_SYMBOL } from '../constants.js';
+import {
+ CTX_DESTROYED,
+ LEGACY_DERIVED_PROP,
+ LEGACY_PROPS,
+ STATE_SYMBOL,
+ TEARDOWN_PROPS
+} from '../constants.js';
import { proxy } from '../proxy.js';
import { capture_store_binding } from './store.js';
import { legacy_mode_flag } from '../../flags/index.js';
@@ -173,6 +179,12 @@ const spread_props_handler = {
}
},
set(target, key, value) {
+ // If the spread props have been torn down, then replace the existing props with
+ // the stale props from the teardown
+ if (key === TEARDOWN_PROPS) {
+ target.props = [value];
+ return true;
+ }
let i = target.props.length;
while (i--) {
let p = target.props[i];
@@ -215,6 +227,9 @@ const spread_props_handler = {
}
},
has(target, key) {
+ if (key === TEARDOWN_PROPS) {
+ return true;
+ }
// To prevent a false positive `is_entry_props` in the `prop` function
if (key === STATE_SYMBOL || key === LEGACY_PROPS) return false;
@@ -368,12 +383,6 @@ export function prop(props, key, flags, fallback) {
// source is written to from various places to persist this value.
var inner_current_value = mutable_source(prop_value);
var current_value = derived(() => {
- var ctx = component_context;
-
- if (ctx !== null && (ctx.f & CTX_DESTROYED) !== 0) {
- return get(inner_current_value);
- }
-
var parent_value = getter();
var child_value = get(inner_current_value);
@@ -418,6 +427,12 @@ export function prop(props, key, flags, fallback) {
return value;
}
+
+ // If the prop is read, we might need to return the stale value if component ctx has been destroyed
+ if (current_value.ctx !== null && (current_value.ctx.f & CTX_DESTROYED) !== 0) {
+ return current_value.v;
+ }
+
return get(current_value);
};
}
diff --git a/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/Component.svelte b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/Component.svelte
new file mode 100644
index 0000000000..e72a19e946
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/Component.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/_config.js b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/_config.js
new file mode 100644
index 0000000000..03a9822cda
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/_config.js
@@ -0,0 +1,11 @@
+import { test } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ async test({ assert, target, logs }) {
+ const [btn1] = target.querySelectorAll('button');
+
+ btn1.click();
+ flushSync();
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/main.svelte
new file mode 100644
index 0000000000..85fab11ab1
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access-3/main.svelte
@@ -0,0 +1,19 @@
+
+
+{#if state}
+ {@const attributes = { title: state.title }}
+
+{/if}
+
diff --git a/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access/_config.js b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access/_config.js
index 794d1f7017..7d1e67e212 100644
--- a/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/ondestroy-prop-access/_config.js
@@ -61,7 +61,7 @@ export default test({
true,
2,
true,
- 1,
+ 2,
true
]);
}