@@ -133,6 +133,8 @@ import {
133133 commitBeforeMutationLifeCycles as commitBeforeMutationEffectOnFiber ,
134134 commitLifeCycles as commitLayoutEffectOnFiber ,
135135 commitPassiveHookEffects ,
136+ commitPassiveHookUnmountEffects ,
137+ commitPassiveHookMountEffects ,
136138 commitPlacement ,
137139 commitWork ,
138140 commitDeletion ,
@@ -2222,34 +2224,116 @@ function flushPassiveEffectsImpl() {
22222224 invokeGuardedCallback ( null , destroy , null ) ;
22232225 }
22242226 pendingUnmountedPassiveEffectDestroyFunctions . length = 0 ;
2225- }
22262227
2227- // Note: This currently assumes there are no passive effects on the root
2228- // fiber, because the root is not part of its own effect list. This could
2229- // change in the future.
2230- let effect = root . current . firstEffect ;
2231- while ( effect !== null ) {
2232- if ( __DEV__ ) {
2233- setCurrentDebugFiberInDEV ( effect ) ;
2234- invokeGuardedCallback ( null , commitPassiveHookEffects , null , effect ) ;
2235- if ( hasCaughtError ( ) ) {
2236- invariant ( effect !== null , 'Should be working on an effect.' ) ;
2237- const error = clearCaughtError ( ) ;
2238- captureCommitPhaseError ( effect , error ) ;
2228+ // It's important that ALL pending passive effect destroy functions are called
2229+ // before ANY passive effect create functions are called.
2230+ // Otherwise effects in sibling components might interfere with each other.
2231+ // e.g. a destroy function in one component may unintentionally override a ref
2232+ // value set by a create function in another component.
2233+ // Layout effects have the same constraint.
2234+
2235+ // First pass: Destroy stale passive effects.
2236+ //
2237+ // Note: This currently assumes there are no passive effects on the root fiber
2238+ // because the root is not part of its own effect list.
2239+ // This could change in the future.
2240+ let effect = root . current . firstEffect ;
2241+ let effectWithErrorDuringUnmount = null ;
2242+ while ( effect !== null ) {
2243+ if ( __DEV__ ) {
2244+ setCurrentDebugFiberInDEV ( effect ) ;
2245+ invokeGuardedCallback (
2246+ null ,
2247+ commitPassiveHookUnmountEffects ,
2248+ null ,
2249+ effect ,
2250+ ) ;
2251+ if ( hasCaughtError ( ) ) {
2252+ effectWithErrorDuringUnmount = effect ;
2253+ invariant ( effect !== null , 'Should be working on an effect.' ) ;
2254+ const error = clearCaughtError ( ) ;
2255+ captureCommitPhaseError ( effect , error ) ;
2256+ }
2257+ resetCurrentDebugFiberInDEV ( ) ;
2258+ } else {
2259+ try {
2260+ commitPassiveHookUnmountEffects ( effect ) ;
2261+ } catch ( error ) {
2262+ effectWithErrorDuringUnmount = effect ;
2263+ invariant ( effect !== null , 'Should be working on an effect.' ) ;
2264+ captureCommitPhaseError ( effect , error ) ;
2265+ }
22392266 }
2240- resetCurrentDebugFiberInDEV ( ) ;
2241- } else {
2242- try {
2243- commitPassiveHookEffects ( effect ) ;
2244- } catch ( error ) {
2245- invariant ( effect !== null , 'Should be working on an effect.' ) ;
2246- captureCommitPhaseError ( effect , error ) ;
2267+ effect = effect . nextEffect ;
2268+ }
2269+
2270+ // Second pass: Create new passive effects.
2271+ //
2272+ // Note: This currently assumes there are no passive effects on the root fiber
2273+ // because the root is not part of its own effect list.
2274+ // This could change in the future.
2275+ effect = root . current . firstEffect ;
2276+ while ( effect !== null ) {
2277+ // Don't run create effects for a Fiber that errored during destroy.
2278+ // This check is in place to match previous behavior.
2279+ // TODO: Rethink whether we want to carry this behavior forward.
2280+ if ( effectWithErrorDuringUnmount !== effect ) {
2281+ if ( __DEV__ ) {
2282+ setCurrentDebugFiberInDEV ( effect ) ;
2283+ invokeGuardedCallback (
2284+ null ,
2285+ commitPassiveHookMountEffects ,
2286+ null ,
2287+ effect ,
2288+ ) ;
2289+ if ( hasCaughtError ( ) ) {
2290+ invariant ( effect !== null , 'Should be working on an effect.' ) ;
2291+ const error = clearCaughtError ( ) ;
2292+ captureCommitPhaseError ( effect , error ) ;
2293+ }
2294+ resetCurrentDebugFiberInDEV ( ) ;
2295+ } else {
2296+ try {
2297+ commitPassiveHookMountEffects ( effect ) ;
2298+ } catch ( error ) {
2299+ invariant ( effect !== null , 'Should be working on an effect.' ) ;
2300+ captureCommitPhaseError ( effect , error ) ;
2301+ }
2302+ }
2303+ }
2304+ const nextNextEffect = effect . nextEffect ;
2305+ // Remove nextEffect pointer to assist GC
2306+ effect . nextEffect = null ;
2307+ effect = nextNextEffect ;
2308+ }
2309+ } else {
2310+ // Note: This currently assumes there are no passive effects on the root fiber
2311+ // because the root is not part of its own effect list.
2312+ // This could change in the future.
2313+ let effect = root . current . firstEffect ;
2314+ while ( effect !== null ) {
2315+ if ( __DEV__ ) {
2316+ setCurrentDebugFiberInDEV ( effect ) ;
2317+ invokeGuardedCallback ( null , commitPassiveHookEffects , null , effect ) ;
2318+ if ( hasCaughtError ( ) ) {
2319+ invariant ( effect !== null , 'Should be working on an effect.' ) ;
2320+ const error = clearCaughtError ( ) ;
2321+ captureCommitPhaseError ( effect , error ) ;
2322+ }
2323+ resetCurrentDebugFiberInDEV ( ) ;
2324+ } else {
2325+ try {
2326+ commitPassiveHookEffects ( effect ) ;
2327+ } catch ( error ) {
2328+ invariant ( effect !== null , 'Should be working on an effect.' ) ;
2329+ captureCommitPhaseError ( effect , error ) ;
2330+ }
22472331 }
2332+ const nextNextEffect = effect . nextEffect ;
2333+ // Remove nextEffect pointer to assist GC
2334+ effect . nextEffect = null ;
2335+ effect = nextNextEffect ;
22482336 }
2249- const nextNextEffect = effect . nextEffect ;
2250- // Remove nextEffect pointer to assist GC
2251- effect . nextEffect = null ;
2252- effect = nextNextEffect ;
22532337 }
22542338
22552339 if ( enableSchedulerTracing ) {
0 commit comments