@@ -180,11 +180,29 @@ export type Hook = {
180180 next : Hook | null ,
181181} ;
182182
183+ // The effect "instance" is a shared object that remains the same for the entire
184+ // lifetime of an effect. In Rust terms, a RefCell. We use it to store the
185+ // "destroy" function that is returned from an effect, because that is stateful.
186+ // The field is `undefined` if the effect is unmounted, or if the effect ran
187+ // but is not stateful. We don't explicitly track whether the effect is mounted
188+ // or unmounted because that can be inferred by the hiddenness of the fiber in
189+ // the tree, i.e. whether there is a hidden Offscreen fiber above it.
190+ //
191+ // It's unfortunate that this is stored on a separate object, because it adds
192+ // more memory per effect instance, but it's conceptually sound. I think there's
193+ // likely a better data structure we could use for effects; perhaps just one
194+ // array of effect instances per fiber. But I think this is OK for now despite
195+ // the additional memory and we can follow up with performance
196+ // optimizations later.
197+ type EffectInstance = {
198+ destroy : void | ( ( ) => void ) ,
199+ } ;
200+
183201export type Effect = {
184202 tag : HookFlags ,
185203 create : ( ) => ( ( ) => void ) | void ,
186- destroy : ( ( ) => void ) | void ,
187- deps : Array < mixed > | void | null ,
204+ inst : EffectInstance ,
205+ deps : Array < mixed > | null ,
188206 next : Effect ,
189207} ;
190208
@@ -1662,7 +1680,7 @@ function mountSyncExternalStore<T>(
16621680 pushEffect(
16631681 HookHasEffect | HookPassive,
16641682 updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot),
1665- undefined ,
1683+ createEffectInstance() ,
16661684 null,
16671685 );
16681686
@@ -1719,7 +1737,7 @@ function updateSyncExternalStore<T>(
17191737 pushEffect (
17201738 HookHasEffect | HookPassive ,
17211739 updateStoreInstance . bind ( null , fiber , inst , nextSnapshot , getSnapshot ) ,
1722- undefined ,
1740+ createEffectInstance ( ) ,
17231741 null ,
17241742 ) ;
17251743
@@ -1860,13 +1878,13 @@ function rerenderState<S>(
18601878function pushEffect(
18611879 tag: HookFlags,
18621880 create: () => ( ( ) => void ) | void ,
1863- destroy : ( ( ) => void ) | void ,
1864- deps : Array < mixed > | void | null,
1881+ inst : EffectInstance ,
1882+ deps : Array < mixed > | null,
18651883): Effect {
18661884 const effect : Effect = {
18671885 tag,
18681886 create,
1869- destroy ,
1887+ inst ,
18701888 deps,
18711889 // Circular
18721890 next : ( null : any ) ,
@@ -1891,6 +1909,10 @@ function pushEffect(
18911909 return effect;
18921910}
18931911
1912+ function createEffectInstance ( ) : EffectInstance {
1913+ return { destroy : undefined } ;
1914+ }
1915+
18941916let stackContainsErrorMessage: boolean | null = null;
18951917
18961918function getCallerStackFrame(): string {
@@ -1994,7 +2016,7 @@ function mountEffectImpl(
19942016 hook . memoizedState = pushEffect (
19952017 HookHasEffect | hookFlags ,
19962018 create ,
1997- undefined ,
2019+ createEffectInstance ( ) ,
19982020 nextDeps ,
19992021 ) ;
20002022}
@@ -2007,16 +2029,16 @@ function updateEffectImpl(
20072029): void {
20082030 const hook = updateWorkInProgressHook ( ) ;
20092031 const nextDeps = deps === undefined ? null : deps ;
2010- let destroy = undefined ;
2032+ const effect : Effect = hook . memoizedState ;
2033+ const inst = effect . inst ;
20112034
20122035 // currentHook is null when rerendering after a render phase state update.
20132036 if ( currentHook !== null ) {
2014- const prevEffect = currentHook . memoizedState ;
2015- destroy = prevEffect . destroy ;
20162037 if ( nextDeps !== null ) {
2038+ const prevEffect : Effect = currentHook . memoizedState ;
20172039 const prevDeps = prevEffect . deps ;
20182040 if ( areHookInputsEqual ( nextDeps , prevDeps ) ) {
2019- hook . memoizedState = pushEffect ( hookFlags , create , destroy , nextDeps ) ;
2041+ hook . memoizedState = pushEffect ( hookFlags , create , inst , nextDeps ) ;
20202042 return ;
20212043 }
20222044 }
@@ -2027,7 +2049,7 @@ function updateEffectImpl(
20272049 hook . memoizedState = pushEffect (
20282050 HookHasEffect | hookFlags ,
20292051 create ,
2030- destroy ,
2052+ inst ,
20312053 nextDeps ,
20322054 ) ;
20332055}
0 commit comments