1
1
/** @import { ComponentContext } from '#client' */
2
- /** @import { Derived, Source } from './types.js' */
2
+ /** @import { Derived, Effect, Source } from './types.js' */
3
3
import { DEV } from 'esm-env' ;
4
4
import {
5
5
PROPS_IS_BINDABLE ,
@@ -12,9 +12,15 @@ import {
12
12
import { get_descriptor , is_function } from '../../shared/utils.js' ;
13
13
import { set , source , update } from './sources.js' ;
14
14
import { derived , derived_safe_equal } from './deriveds.js' ;
15
- import { get , is_destroying_effect , untrack } from '../runtime.js' ;
15
+ import {
16
+ active_effect ,
17
+ get ,
18
+ is_destroying_effect ,
19
+ set_active_effect ,
20
+ untrack
21
+ } from '../runtime.js' ;
16
22
import * as e from '../errors.js' ;
17
- import { LEGACY_PROPS , STATE_SYMBOL } from '#client/constants' ;
23
+ import { DESTROYED , LEGACY_PROPS , STATE_SYMBOL } from '#client/constants' ;
18
24
import { proxy } from '../proxy.js' ;
19
25
import { capture_store_binding } from './store.js' ;
20
26
import { legacy_mode_flag } from '../../flags/index.js' ;
@@ -94,7 +100,7 @@ export function rest_props(props, exclude, name) {
94
100
95
101
/**
96
102
* The proxy handler for legacy $$restProps and $$props
97
- * @type {ProxyHandler<{ props: Record<string | symbol, unknown>, exclude: Array<string | symbol>, special: Record<string | symbol, (v?: unknown) => unknown>, version: Source<number> }> }}
103
+ * @type {ProxyHandler<{ props: Record<string | symbol, unknown>, exclude: Array<string | symbol>, special: Record<string | symbol, (v?: unknown) => unknown>, version: Source<number>, parent_effect: Effect }> }}
98
104
*/
99
105
const legacy_rest_props_handler = {
100
106
get ( target , key ) {
@@ -104,17 +110,25 @@ const legacy_rest_props_handler = {
104
110
} ,
105
111
set ( target , key , value ) {
106
112
if ( ! ( key in target . special ) ) {
107
- // Handle props that can temporarily get out of sync with the parent
108
- /** @type {Record<string, (v?: unknown) => unknown> } */
109
- target . special [ key ] = prop (
110
- {
111
- get [ key ] ( ) {
112
- return target . props [ key ] ;
113
- }
114
- } ,
115
- /** @type {string } */ ( key ) ,
116
- PROPS_IS_UPDATED
117
- ) ;
113
+ var previous_effect = active_effect ;
114
+
115
+ try {
116
+ set_active_effect ( target . parent_effect ) ;
117
+
118
+ // Handle props that can temporarily get out of sync with the parent
119
+ /** @type {Record<string, (v?: unknown) => unknown> } */
120
+ target . special [ key ] = prop (
121
+ {
122
+ get [ key ] ( ) {
123
+ return target . props [ key ] ;
124
+ }
125
+ } ,
126
+ /** @type {string } */ ( key ) ,
127
+ PROPS_IS_UPDATED
128
+ ) ;
129
+ } finally {
130
+ set_active_effect ( previous_effect ) ;
131
+ }
118
132
}
119
133
120
134
target . special [ key ] ( value ) ;
@@ -153,7 +167,19 @@ const legacy_rest_props_handler = {
153
167
* @returns {Record<string, unknown> }
154
168
*/
155
169
export function legacy_rest_props ( props , exclude ) {
156
- return new Proxy ( { props, exclude, special : { } , version : source ( 0 ) } , legacy_rest_props_handler ) ;
170
+ return new Proxy (
171
+ {
172
+ props,
173
+ exclude,
174
+ special : { } ,
175
+ version : source ( 0 ) ,
176
+ // TODO this is only necessary because we need to track component
177
+ // destruction inside `prop`, because of `bind:this`, but it
178
+ // seems likely that we can simplify `bind:this` instead
179
+ parent_effect : /** @type {Effect } */ ( active_effect )
180
+ } ,
181
+ legacy_rest_props_handler
182
+ ) ;
157
183
}
158
184
159
185
/**
@@ -376,6 +402,12 @@ export function prop(props, key, flags, fallback) {
376
402
// Capture the initial value if it's bindable
377
403
if ( bindable ) get ( d ) ;
378
404
405
+ var parent_effect = /** @type {Effect } */ ( active_effect ) ;
406
+
407
+ if ( ! parent_effect ) {
408
+ console . trace ( ) ;
409
+ }
410
+
379
411
return function ( /** @type {any } */ value , /** @type {boolean } */ mutation ) {
380
412
if ( arguments . length > 0 ) {
381
413
const new_value = mutation ? get ( d ) : runes && bindable ? proxy ( value ) : value ;
@@ -390,10 +422,12 @@ export function prop(props, key, flags, fallback) {
390
422
return value ;
391
423
}
392
424
393
- // special case — avoid recalculating the derived if
394
- // we're in a teardown function and the prop
395
- // was overridden locally
396
- if ( is_destroying_effect && overridden ) {
425
+ // special case — avoid recalculating the derived if we're in a
426
+ // teardown function and the prop was overridden locally, or the
427
+ // component was already destroyed (this latter part is necessary
428
+ // because `bind:this` can read props after the component has
429
+ // been destroyed. TODO simplify `bind:this`
430
+ if ( ( is_destroying_effect && overridden ) || ( parent_effect . f & DESTROYED ) !== 0 ) {
397
431
return d . v ;
398
432
}
399
433
0 commit comments