173
} from './ReactFiberDevToolsHook.new';
174
import {releaseCache, retainCache} from './ReactFiberCacheComponent.new';
175
import {clearTransitionsForLanes} from './ReactFiberLane.new';
176
+import {
177
+ OffscreenVisible,
178
+ OffscreenPassiveEffectsConnected,
179
+} from './ReactFiberOffscreenComponent';
180
181
let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
182
if (__DEV__) {
2428
const offscreenFiber: Fiber = (finishedWork.child: any);
2429
2430
if (offscreenFiber.flags & Visibility) {
2427
- const offscreenInstance: OffscreenInstance = offscreenFiber.stateNode;
2431
const newState: OffscreenState | null = offscreenFiber.memoizedState;
2432
const isHidden = newState !== null;
2430
-
2431
- // Track the current state on the Offscreen instance so we can
2432
- // read it during an event
2433
- offscreenInstance.isHidden = isHidden;
2434
-
2433
if (isHidden) {
2434
const wasHidden =
2435
offscreenFiber.alternate !== null &&
2483
2484
// Track the current state on the Offscreen instance so we can
2485
// read it during an event
2488
- offscreenInstance.isHidden = isHidden;
2486
+ if (isHidden) {
2487
+ offscreenInstance.visibility &= ~OffscreenVisible;
2488
+ } else {
2489
+ offscreenInstance.visibility |= OffscreenVisible;
2490
+ }
2491
2492
if (isHidden) {
2493
if (!wasHidden) {
2873
setCurrentDebugFiberInDEV(prevDebugFiber);
2874
}
2875
2876
+function commitHookPassiveMountEffects(
2877
+ finishedWork: Fiber,
2878
+ hookFlags: HookFlags,
2879
+) {
2880
+ if (
2881
+ enableProfilerTimer &&
2882
+ enableProfilerCommitHooks &&
2883
+ finishedWork.mode & ProfileMode
2884
+ ) {
2885
+ startPassiveEffectTimer();
2886
+ try {
2887
+ commitHookEffectListMount(hookFlags, finishedWork);
2888
+ } catch (error) {
2889
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
2890
+ }
2891
+ recordPassiveEffectDuration(finishedWork);
2892
+ } else {
2893
+ try {
2894
+ commitHookEffectListMount(hookFlags, finishedWork);
2895
+ } catch (error) {
2896
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
2897
+ }
2898
+ }
2899
+}
2900
+
2901
+function commitOffscreenPassiveMountEffects(
2902
+ current: Fiber | null,
2903
+ finishedWork: Fiber,
2904
+ instance: OffscreenInstance,
2905
+) {
2906
+ if (enableCache) {
2907
+ let previousCache: Cache | null = null;
2908
+ if (
2909
+ current !== null &&
2910
+ current.memoizedState !== null &&
2911
+ current.memoizedState.cachePool !== null
2912
+ ) {
2913
+ previousCache = current.memoizedState.cachePool.pool;
2914
+ }
2915
+ let nextCache: Cache | null = null;
2916
+ if (
2917
+ finishedWork.memoizedState !== null &&
2918
+ finishedWork.memoizedState.cachePool !== null
2919
+ ) {
2920
+ nextCache = finishedWork.memoizedState.cachePool.pool;
2921
+ }
2922
+ // Retain/release the cache used for pending (suspended) nodes.
2923
+ // Note that this is only reached in the non-suspended/visible case:
2924
+ // when the content is suspended/hidden, the retain/release occurs
2925
+ // via the parent Suspense component (see case above).
2926
+ if (nextCache !== previousCache) {
2927
+ if (nextCache != null) {
2928
+ retainCache(nextCache);
2929
+ }
2930
+ if (previousCache != null) {
2931
+ releaseCache(previousCache);
2932
+ }
2933
+ }
2934
+ }
2935
+
2936
+ if (enableTransitionTracing) {
2937
+ // TODO: Pre-rendering should not be counted as part of a transition. We
2938
+ // may add separate logs for pre-rendering, but it's not part of the
2939
+ // primary metrics.
2940
+ const offscreenState: OffscreenState = finishedWork.memoizedState;
2941
+ const queue: OffscreenQueue | null = (finishedWork.updateQueue: any);
2942
+
2943
+ const isHidden = offscreenState !== null;
2944
+ if (queue !== null) {
2945
+ if (isHidden) {
2946
+ const transitions = queue.transitions;
2947
+ if (transitions !== null) {
2948
+ transitions.forEach(transition => {
2949
+ // Add all the transitions saved in the update queue during
2950
+ // the render phase (ie the transitions associated with this boundary)
2951
+ // into the transitions set.
2952
+ if (instance.transitions === null) {
2953
+ instance.transitions = new Set();
2954
+ }
2955
+ instance.transitions.add(transition);
2956
+ });
2957
+ }
2958
+
2959
+ const markerInstances = queue.markerInstances;
2960
+ if (markerInstances !== null) {
2961
+ markerInstances.forEach(markerInstance => {
2962
+ const markerTransitions = markerInstance.transitions;
2963
+ // There should only be a few tracing marker transitions because
2964
+ // they should be only associated with the transition that
2965
+ // caused them
2966
+ if (markerTransitions !== null) {
2967
+ markerTransitions.forEach(transition => {
2968
+ if (instance.transitions === null) {
2969
+ instance.transitions = new Set();
2970
+ } else if (instance.transitions.has(transition)) {
2971
+ if (markerInstance.pendingBoundaries === null) {
2972
+ markerInstance.pendingBoundaries = new Map();
2973
+ }
2974
+ if (instance.pendingMarkers === null) {
2975
+ instance.pendingMarkers = new Set();
2976
+ }
2977
+
2978
+ instance.pendingMarkers.add(markerInstance);
2979
+ }
2980
+ });
2981
+ }
2982
+ });
2983
+ }
2984
+ }
2985
+
2986
+ finishedWork.updateQueue = null;
2987
+ }
2988
+
2989
+ commitTransitionProgress(finishedWork);
2990
+ }
2991
+}
2992
+
2993
+function commitCachePassiveMountEffect(
2994
+ current: Fiber | null,
2995
+ finishedWork: Fiber,
2996
+) {
2997
+ if (enableCache) {
2998
+ let previousCache: Cache | null = null;
2999
+ if (finishedWork.alternate !== null) {
3000
+ previousCache = finishedWork.alternate.memoizedState.cache;
3001
+ }
3002
+ const nextCache = finishedWork.memoizedState.cache;
3003
+ // Retain/release the cache. In theory the cache component
3004
+ // could be "borrowing" a cache instance owned by some parent,
3005
+ // in which case we could avoid retaining/releasing. But it
3006
+ // is non-trivial to determine when that is the case, so we
3007
+ // always retain/release.
3008
+ if (nextCache !== previousCache) {
3009
+ retainCache(nextCache);
3010
+ if (previousCache != null) {
3011
+ releaseCache(previousCache);
3012
+ }
3013
+ }
3014
+ }
3015
+}
3016
+
3017
+function commitTracingMarkerPassiveMountEffect(finishedWork: Fiber) {
3018
+ // Get the transitions that were initiatized during the render
3019
+ // and add a start transition callback for each of them
3020
+ const instance = finishedWork.stateNode;
3021
+ if (
3022
+ instance.transitions !== null &&
3023
+ (instance.pendingBoundaries === null ||
3024
+ instance.pendingBoundaries.size === 0)
3025
+ ) {
3026
+ instance.transitions.forEach(transition => {
3027
+ addMarkerCompleteCallbackToPendingTransition(
3028
+ finishedWork.memoizedProps.name,
3029
+ instance.transitions,
3030
+ );
3031
+ });
3032
+ instance.transitions = null;
3033
+ instance.pendingBoundaries = null;
3034
+ }
3035
+}
3036
+
3037
export function commitPassiveMountEffects(
3038
root: FiberRoot,
3039
finishedWork: Fiber,
3079
committedLanes: Lanes,
3080
committedTransitions: Array<Transition> | null,
3081
): void {
3082
+ // When updating this function, also update reconnectPassiveEffects, which does
3083
+ // most of the same things when an offscreen tree goes from hidden -> visible,
3084
+ // or when toggling effects inside a hidden tree.
3085
const flags = finishedWork.flags;
3086
switch (finishedWork.tag) {
3087
case FunctionComponent:
3094
committedTransitions,
3095
);
3096
if (flags & Passive) {
2931
- if (
2932
- enableProfilerTimer &&
2933
- enableProfilerCommitHooks &&
2934
- finishedWork.mode & ProfileMode
2935
- ) {
2936
- startPassiveEffectTimer();
2937
- try {
2938
- commitHookEffectListMount(
2939
- HookPassive | HookHasEffect,
2940
- finishedWork,
2941
- );
2942
- } catch (error) {
2943
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2944
- }
2945
- recordPassiveEffectDuration(finishedWork);
2946
- } else {
2947
- try {
2948
- commitHookEffectListMount(
2949
- HookPassive | HookHasEffect,
2950
- finishedWork,
2951
- );
2952
- } catch (error) {
2953
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
2954
- }
2955
- }
3097
+ commitHookPassiveMountEffects(
3098
+ finishedWork,
3099
+ HookPassive | HookHasEffect,
3100
+ );
3101
}
3102
break;
3103
}
3158
}
3159
case LegacyHiddenComponent:
3160
case OffscreenComponent: {
3161
+ // TODO: Pass `current` as argument to this function
3162
+ const instance: OffscreenInstance = finishedWork.stateNode;
3163
+ const nextState: OffscreenState | null = finishedWork.memoizedState;
3164
+
3165
+ const isHidden = nextState !== null;
3166
+
3167
+ if (isHidden) {
3168
+ if (instance.visibility & OffscreenPassiveEffectsConnected) {
3169
+ // The effects are currently connected. Update them.
3170
+ recursivelyTraversePassiveMountEffects(
3171
+ finishedRoot,
3172
+ finishedWork,
3173
+ committedLanes,
3174
+ committedTransitions,
3175
+ );
3176
+ } else {
3177
+ if (finishedWork.mode & ConcurrentMode) {
3178
+ // The effects are currently disconnected. Since the tree is hidden,
3179
+ // don't connect them. This also applies to the initial render.
3180
+ if (enableCache || enableTransitionTracing) {
3181
+ // "Atomic" effects are ones that need to fire on every commit,
3182
+ // even during pre-rendering. An example is updating the reference
3183
+ // count on cache instances.
3184
+ recursivelyTraverseAtomicPassiveEffects(
3185
+ finishedRoot,
3186
+ finishedWork,
3187
+ committedLanes,
3188
+ committedTransitions,
3189
+ );
3190
+ }
3191
+ } else {
3192
+ // Legacy Mode: Fire the effects even if the tree is hidden.
3193
+ instance.visibility |= OffscreenPassiveEffectsConnected;
3194
+ recursivelyTraversePassiveMountEffects(
3195
+ finishedRoot,
3196
+ finishedWork,
3197
+ committedLanes,
3198
+ committedTransitions,
3199
+ );
3200
+ }
3201
+ }
3202
+ } else {
3203
+ // Tree is visible
3204
+ if (instance.visibility & OffscreenPassiveEffectsConnected) {
3205
+ // The effects are currently connected. Update them.
3206
+ recursivelyTraversePassiveMountEffects(
3207
+ finishedRoot,
3208
+ finishedWork,
3209
+ committedLanes,
3210
+ committedTransitions,
3211
+ );
3212
+ } else {
3213
+ // The effects are currently disconnected. Reconnect them, while also
3214
+ // firing effects inside newly mounted trees. This also applies to
3215
+ // the initial render.
3216
+ instance.visibility |= OffscreenPassiveEffectsConnected;
3217
+
3218
+ const includeWorkInProgressEffects =
3219
+ (finishedWork.subtreeFlags & PassiveMask) !== NoFlags;
3220
+ recursivelyTraverseReconnectPassiveEffects(
3221
+ finishedRoot,
3222
+ finishedWork,
3223
+ committedLanes,
3224
+ committedTransitions,
3225
+ includeWorkInProgressEffects,
3226
+ );
3227
+ }
3228
+ }
3229
+
3230
+ if (flags & Passive) {
3231
+ const current = finishedWork.alternate;
3232
+ commitOffscreenPassiveMountEffects(current, finishedWork, instance);
3233
+ }
3234
+ break;
3235
+ }
3236
+ case CacheComponent: {
3237
recursivelyTraversePassiveMountEffects(
3238
finishedRoot,
3239
finishedWork,
3241
committedTransitions,
3242
);
3243
if (flags & Passive) {
3023
- if (enableCache) {
3024
- let previousCache: Cache | null = null;
3025
- if (
3026
- finishedWork.alternate !== null &&
3027
- finishedWork.alternate.memoizedState !== null &&
3028
- finishedWork.alternate.memoizedState.cachePool !== null
3029
- ) {
3030
- previousCache = finishedWork.alternate.memoizedState.cachePool.pool;
3031
- }
3032
- let nextCache: Cache | null = null;
3033
- if (
3034
- finishedWork.memoizedState !== null &&
3035
- finishedWork.memoizedState.cachePool !== null
3036
- ) {
3037
- nextCache = finishedWork.memoizedState.cachePool.pool;
3038
- }
3039
- // Retain/release the cache used for pending (suspended) nodes.
3040
- // Note that this is only reached in the non-suspended/visible case:
3041
- // when the content is suspended/hidden, the retain/release occurs
3042
- // via the parent Suspense component (see case above).
3043
- if (nextCache !== previousCache) {
3044
- if (nextCache != null) {
3045
- retainCache(nextCache);
3046
- }
3047
- if (previousCache != null) {
3048
- releaseCache(previousCache);
3049
- }
3050
- }
3244
+ // TODO: Pass `current` as argument to this function
3245
+ const current = finishedWork.alternate;
3246
+ commitCachePassiveMountEffect(current, finishedWork);
3247
+ }
3248
+ break;
3249
+ }
3250
+ case TracingMarkerComponent: {
3251
+ if (enableTransitionTracing) {
3252
+ recursivelyTraversePassiveMountEffects(
3253
+ finishedRoot,
3254
+ finishedWork,
3255
+ committedLanes,
3256
+ committedTransitions,
3257
+ );
3258
+ if (flags & Passive) {
3259
+ commitTracingMarkerPassiveMountEffect(finishedWork);
3260
}
3261
+ break;
3262
+ }
3263
+ // Intentional fallthrough to next branch
3264
+ }
3265
+ // eslint-disable-next-line-no-fallthrough
3266
+ default: {
3267
+ recursivelyTraversePassiveMountEffects(
3268
+ finishedRoot,
3269
+ finishedWork,
3270
+ committedLanes,
3271
+ committedTransitions,
3272
+ );
3273
+ break;
3274
+ }
3275
+ }
3276
+}
3277
3053
- if (enableTransitionTracing) {
3054
- const isFallback = finishedWork.memoizedState;
3055
- const queue: OffscreenQueue | null = (finishedWork.updateQueue: any);
3056
- const instance: OffscreenInstance = finishedWork.stateNode;
3057
-
3058
- if (queue !== null) {
3059
- if (isFallback) {
3060
- const transitions = queue.transitions;
3061
- if (transitions !== null) {
3062
- transitions.forEach(transition => {
3063
- // Add all the transitions saved in the update queue during
3064
- // the render phase (ie the transitions associated with this boundary)
3065
- // into the transitions set.
3066
- if (instance.transitions === null) {
3067
- instance.transitions = new Set();
3068
- }
3069
- instance.transitions.add(transition);
3070
- });
3071
- }
3278
+function recursivelyTraverseReconnectPassiveEffects(
3279
+ finishedRoot: FiberRoot,
3280
+ parentFiber: Fiber,
3281
+ committedLanes: Lanes,
3282
+ committedTransitions: Array<Transition> | null,
3283
+ includeWorkInProgressEffects: boolean,
3284
+) {
3285
+ // This function visits both newly finished work and nodes that were re-used
3286
+ // from a previously committed tree. We cannot check non-static flags if the
3287
+ // node was reused.
3288
+ const childShouldIncludeWorkInProgressEffects =
3289
+ includeWorkInProgressEffects &&
3290
+ (parentFiber.subtreeFlags & PassiveMask) !== NoFlags;
3291
3073
- const markerInstances = queue.markerInstances;
3074
- if (markerInstances !== null) {
3075
- markerInstances.forEach(markerInstance => {
3076
- const markerTransitions = markerInstance.transitions;
3077
- // There should only be a few tracing marker transitions because
3078
- // they should be only associated with the transition that
3079
- // caused them
3080
- if (markerTransitions !== null) {
3081
- markerTransitions.forEach(transition => {
3082
- if (instance.transitions === null) {
3083
- instance.transitions = new Set();
3084
- } else if (instance.transitions.has(transition)) {
3085
- if (markerInstance.pendingBoundaries === null) {
3086
- markerInstance.pendingBoundaries = new Map();
3087
- }
3088
- if (instance.pendingMarkers === null) {
3089
- instance.pendingMarkers = new Set();
3090
- }
3091
-
3092
- instance.pendingMarkers.add(markerInstance);
3093
- }
3094
- });
3095
- }
3096
- });
3097
- }
3098
- }
3292
+ // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
3293
+ const prevDebugFiber = getCurrentDebugFiberInDEV();
3294
+ let child = parentFiber.child;
3295
+ while (child !== null) {
3296
+ reconnectPassiveEffects(
3297
+ finishedRoot,
3298
+ child,
3299
+ committedLanes,
3300
+ committedTransitions,
3301
+ childShouldIncludeWorkInProgressEffects,
3302
+ );
3303
+ child = child.sibling;
3304
+ }
3305
+ setCurrentDebugFiberInDEV(prevDebugFiber);
3306
+}
3307
3100
- finishedWork.updateQueue = null;
3308
+function reconnectPassiveEffects(
3309
+ finishedRoot: FiberRoot,
3310
+ finishedWork: Fiber,
3311
+ committedLanes: Lanes,
3312
+ committedTransitions: Array<Transition> | null,
3313
+ // This function visits both newly finished work and nodes that were re-used
3314
+ // from a previously committed tree. We cannot check non-static flags if the
3315
+ // node was reused.
3316
+ includeWorkInProgressEffects: boolean,
3317
+) {
3318
+ const flags = finishedWork.flags;
3319
+ switch (finishedWork.tag) {
3320
+ case FunctionComponent:
3321
+ case ForwardRef:
3322
+ case SimpleMemoComponent: {
3323
+ recursivelyTraverseReconnectPassiveEffects(
3324
+ finishedRoot,
3325
+ finishedWork,
3326
+ committedLanes,
3327
+ committedTransitions,
3328
+ includeWorkInProgressEffects,
3329
+ );
3330
+ // TODO: Check for PassiveStatic flag
3331
+ commitHookPassiveMountEffects(finishedWork, HookPassive);
3332
+ break;
3333
+ }
3334
+ // Unlike commitPassiveMountOnFiber, we don't need to handle HostRoot
3335
+ // because this function only visits nodes that are inside an
3336
+ // Offscreen fiber.
3337
+ // case HostRoot: {
3338
+ // ...
3339
+ // }
3340
+ case LegacyHiddenComponent:
3341
+ case OffscreenComponent: {
3342
+ const instance: OffscreenInstance = finishedWork.stateNode;
3343
+ const nextState: OffscreenState | null = finishedWork.memoizedState;
3344
+
3345
+ const isHidden = nextState !== null;
3346
+
3347
+ if (isHidden) {
3348
+ if (instance.visibility & OffscreenPassiveEffectsConnected) {
3349
+ // The effects are currently connected. Update them.
3350
+ recursivelyTraverseReconnectPassiveEffects(
3351
+ finishedRoot,
3352
+ finishedWork,
3353
+ committedLanes,
3354
+ committedTransitions,
3355
+ includeWorkInProgressEffects,
3356
+ );
3357
+ } else {
3358
+ if (finishedWork.mode & ConcurrentMode) {
3359
+ // The effects are currently disconnected. Since the tree is hidden,
3360
+ // don't connect them. This also applies to the initial render.
3361
+ if (enableCache || enableTransitionTracing) {
3362
+ // "Atomic" effects are ones that need to fire on every commit,
3363
+ // even during pre-rendering. An example is updating the reference
3364
+ // count on cache instances.
3365
+ recursivelyTraverseAtomicPassiveEffects(
3366
+ finishedRoot,
3367
+ finishedWork,
3368
+ committedLanes,
3369
+ committedTransitions,
3370
+ );
3371
+ }
3372
+ } else {
3373
+ // Legacy Mode: Fire the effects even if the tree is hidden.
3374
+ instance.visibility |= OffscreenPassiveEffectsConnected;
3375
+ recursivelyTraverseReconnectPassiveEffects(
3376
+ finishedRoot,
3377
+ finishedWork,
3378
+ committedLanes,
3379
+ committedTransitions,
3380
+ includeWorkInProgressEffects,
3381
+ );
3382
}
3383
+ }
3384
+ } else {
3385
+ // Tree is visible
3386
+
3387
+ // Since we're already inside a reconnecting tree, it doesn't matter
3388
+ // whether the effects are currently connected. In either case, we'll
3389
+ // continue traversing the tree and firing all the effects.
3390
+ //
3391
+ // We do need to set the "connected" flag on the instance, though.
3392
+ instance.visibility |= OffscreenPassiveEffectsConnected;
3393
3103
- commitTransitionProgress(finishedWork);
3394
+ recursivelyTraverseReconnectPassiveEffects(
3395
+ finishedRoot,
3396
+ finishedWork,
3397
+ committedLanes,
3398
+ committedTransitions,
3399
+ includeWorkInProgressEffects,
3400
+ );
3401
+ }
3402
+
3403
+ if (includeWorkInProgressEffects && flags & Passive) {
3404
+ // TODO: Pass `current` as argument to this function
3405
+ const current: Fiber | null = finishedWork.alternate;
3406
+ commitOffscreenPassiveMountEffects(current, finishedWork, instance);
3407
+ }
3408
+ break;
3409
+ }
3410
+ case CacheComponent: {
3411
+ recursivelyTraverseReconnectPassiveEffects(
3412
+ finishedRoot,
3413
+ finishedWork,
3414
+ committedLanes,
3415
+ committedTransitions,
3416
+ includeWorkInProgressEffects,
3417
+ );
3418
+ if (includeWorkInProgressEffects && flags & Passive) {
3419
+ // TODO: Pass `current` as argument to this function
3420
+ const current = finishedWork.alternate;
3421
+ commitCachePassiveMountEffect(current, finishedWork);
3422
+ }
3423
+ break;
3424
+ }
3425
+ case TracingMarkerComponent: {
3426
+ if (enableTransitionTracing) {
3427
+ recursivelyTraverseReconnectPassiveEffects(
3428
+ finishedRoot,
3429
+ finishedWork,
3430
+ committedLanes,
3431
+ committedTransitions,
3432
+ includeWorkInProgressEffects,
3433
+ );
3434
+ if (includeWorkInProgressEffects && flags & Passive) {
3435
+ commitTracingMarkerPassiveMountEffect(finishedWork);
3436
}
3437
+ break;
3438
+ }
3439
+ // Intentional fallthrough to next branch
3440
+ }
3441
+ // eslint-disable-next-line-no-fallthrough
3442
+ default: {
3443
+ recursivelyTraverseReconnectPassiveEffects(
3444
+ finishedRoot,
3445
+ finishedWork,
3446
+ committedLanes,
3447
+ committedTransitions,
3448
+ includeWorkInProgressEffects,
3449
+ );
3450
+ break;
3451
+ }
3452
+ }
3453
+}
3454
+
3455
+function recursivelyTraverseAtomicPassiveEffects(
3456
+ finishedRoot: FiberRoot,
3457
+ parentFiber: Fiber,
3458
+ committedLanes: Lanes,
3459
+ committedTransitions: Array<Transition> | null,
3460
+) {
3461
+ // "Atomic" effects are ones that need to fire on every commit, even during
3462
+ // pre-rendering. We call this function when traversing a hidden tree whose
3463
+ // regular effects are currently disconnected.
3464
+ const prevDebugFiber = getCurrentDebugFiberInDEV();
3465
+ // TODO: Add special flag for atomic effects
3466
+ if (parentFiber.subtreeFlags & PassiveMask) {
3467
+ let child = parentFiber.child;
3468
+ while (child !== null) {
3469
+ setCurrentDebugFiberInDEV(child);
3470
+ commitAtomicPassiveEffects(
3471
+ finishedRoot,
3472
+ child,
3473
+ committedLanes,
3474
+ committedTransitions,
3475
+ );
3476
+ child = child.sibling;
3477
+ }
3478
+ }
3479
+ setCurrentDebugFiberInDEV(prevDebugFiber);
3480
+}
3481
+
3482
+function commitAtomicPassiveEffects(
3483
+ finishedRoot: FiberRoot,
3484
+ finishedWork: Fiber,
3485
+ committedLanes: Lanes,
3486
+ committedTransitions: Array<Transition> | null,
3487
+) {
3488
+ // "Atomic" effects are ones that need to fire on every commit, even during
3489
+ // pre-rendering. We call this function when traversing a hidden tree whose
3490
+ // regular effects are currently disconnected.
3491
+ const flags = finishedWork.flags;
3492
+ switch (finishedWork.tag) {
3493
+ case OffscreenComponent: {
3494
+ recursivelyTraverseAtomicPassiveEffects(
3495
+ finishedRoot,
3496
+ finishedWork,
3497
+ committedLanes,
3498
+ committedTransitions,
3499
+ );
3500
+ if (flags & Passive) {
3501
+ // TODO: Pass `current` as argument to this function
3502
+ const current = finishedWork.alternate;
3503
+ const instance: OffscreenInstance = finishedWork.stateNode;
3504
+ commitOffscreenPassiveMountEffects(current, finishedWork, instance);
3505
}
3506
break;
3507
}
3508
case CacheComponent: {
3109
- recursivelyTraversePassiveMountEffects(
3509
+ recursivelyTraverseAtomicPassiveEffects(
3510
finishedRoot,
3511
finishedWork,
3512
committedLanes,
3513
committedTransitions,
3514
);
3515
if (flags & Passive) {
3116
- if (enableCache) {
3117
- let previousCache: Cache | null = null;
3118
- if (finishedWork.alternate !== null) {
3119
- previousCache = finishedWork.alternate.memoizedState.cache;
3120
- }
3121
- const nextCache = finishedWork.memoizedState.cache;
3122
- // Retain/release the cache. In theory the cache component
3123
- // could be "borrowing" a cache instance owned by some parent,
3124
- // in which case we could avoid retaining/releasing. But it
3125
- // is non-trivial to determine when that is the case, so we
3126
- // always retain/release.
3127
- if (nextCache !== previousCache) {
3128
- retainCache(nextCache);
3129
- if (previousCache != null) {
3130
- releaseCache(previousCache);
3131
- }
3132
- }
3133
- }
3516
+ // TODO: Pass `current` as argument to this function
3517
+ const current = finishedWork.alternate;
3518
+ commitCachePassiveMountEffect(current, finishedWork);
3519
}
3520
break;
3521
}
3522
case TracingMarkerComponent: {
3523
if (enableTransitionTracing) {
3139
- recursivelyTraversePassiveMountEffects(
3524
+ recursivelyTraverseAtomicPassiveEffects(
3525
finishedRoot,
3526
finishedWork,
3527
committedLanes,
3528
committedTransitions,
3529
);
3530
if (flags & Passive) {
3146
- // Get the transitions that were initiatized during the render
3147
- // and add a start transition callback for each of them
3148
- const instance = finishedWork.stateNode;
3149
- if (
3150
- instance.transitions !== null &&
3151
- (instance.pendingBoundaries === null ||
3152
- instance.pendingBoundaries.size === 0)
3153
- ) {
3154
- instance.transitions.forEach(transition => {
3155
- addMarkerCompleteCallbackToPendingTransition(
3156
- finishedWork.memoizedProps.name,
3157
- instance.transitions,
3158
- );
3159
- });
3160
- instance.transitions = null;
3161
- instance.pendingBoundaries = null;
3162
- }
3531
+ commitTracingMarkerPassiveMountEffect(finishedWork);
3532
}
3533
break;
3534
}
3536
}
3537
// eslint-disable-next-line-no-fallthrough
3538
default: {
3170
- recursivelyTraversePassiveMountEffects(
3539
+ recursivelyTraverseAtomicPassiveEffects(
3540
finishedRoot,
3541
finishedWork,
3542
committedLanes,
3644
}
3645
break;
3646
}
3647
+ // TODO: Disconnect passive effects when a tree is hidden, perhaps after
3648
+ // a delay.
3649
+ // case OffscreenComponent: {
3650
+ // ...
3651
+ // }
3652
default: {
3653
recursivelyTraversePassiveUnmountEffects(finishedWork);
3654
break;