Skip to content

Commit 3f16f8d

Browse files
committed
offscreen double invoke effects
1 parent 5f1890f commit 3f16f8d

13 files changed

+647
-4
lines changed

packages/react-reconciler/src/ReactFiberCommitWork.new.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ const callComponentWillUnmountWithTimer = function(current, instance) {
160160
};
161161

162162
// Capture errors so they don't interrupt unmounting.
163-
function safelyCallComponentWillUnmount(current, instance) {
163+
export function safelyCallComponentWillUnmount(current: Fiber, instance: any) {
164164
if (__DEV__) {
165165
invokeGuardedCallback(
166166
null,
@@ -313,7 +313,7 @@ function commitBeforeMutationLifeCycles(
313313
);
314314
}
315315

316-
function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {
316+
export function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {
317317
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
318318
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
319319
if (lastEffect !== null) {
@@ -325,15 +325,15 @@ function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {
325325
const destroy = effect.destroy;
326326
effect.destroy = undefined;
327327
if (destroy !== undefined) {
328-
destroy();
328+
safelyCallDestroy(finishedWork, destroy);
329329
}
330330
}
331331
effect = effect.next;
332332
} while (effect !== firstEffect);
333333
}
334334
}
335335

336-
function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
336+
export function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
337337
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
338338
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
339339
if (lastEffect !== null) {

packages/react-reconciler/src/ReactFiberWorkLoop.new.js

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
enableDebugTracing,
3333
enableSchedulingProfiler,
3434
enableScopeAPI,
35+
enableDoubleInvokingEffects,
3536
} from 'shared/ReactFeatureFlags';
3637
import ReactSharedInternals from 'shared/ReactSharedInternals';
3738
import invariant from 'shared/invariant';
@@ -55,6 +56,7 @@ import {
5556
NoEffect as NoHookEffect,
5657
HasEffect as HookHasEffect,
5758
Passive as HookPassive,
59+
Layout as HookLayout,
5860
} from './ReactHookEffectTags';
5961
import {
6062
logCommitStarted,
@@ -208,9 +210,12 @@ import {
208210
commitDeletion,
209211
commitDetachRef,
210212
commitAttachRef,
213+
commitHookEffectListUnmount,
214+
commitHookEffectListMount,
211215
commitPassiveEffectDurations,
212216
commitResetTextContent,
213217
isSuspenseBoundaryBeingHidden,
218+
safelyCallComponentWillUnmount,
214219
safelyCallDestroy,
215220
} from './ReactFiberCommitWork.new';
216221
import {enqueueUpdate} from './ReactUpdateQueue.new';
@@ -2273,6 +2278,14 @@ function commitRootImpl(root, renderPriorityLevel) {
22732278
}
22742279
}
22752280

2281+
if (enableDoubleInvokingEffects) {
2282+
if (__DEV__) {
2283+
if (!rootDidHavePassiveEffects) {
2284+
doubleInvokeEffectsInDEV(root.current, false);
2285+
}
2286+
}
2287+
}
2288+
22762289
if (remainingLanes === SyncLane) {
22772290
// Count the number of times the root synchronously re-renders without
22782291
// finishing. If there are too many, it indicates an infinite update loop.
@@ -2970,6 +2983,12 @@ function flushPassiveEffectsImpl() {
29702983
nestedPassiveUpdateCount =
29712984
rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;
29722985

2986+
if (enableDoubleInvokingEffects) {
2987+
if (__DEV__) {
2988+
doubleInvokeEffectsInDEV(root.current, true);
2989+
}
2990+
}
2991+
29732992
return true;
29742993
}
29752994

@@ -3822,6 +3841,195 @@ function finishPendingInteractions(root, committedLanes) {
38223841
}
38233842
}
38243843

3844+
function doubleInvokeEffectsInDEV(fiber, hasPassiveEffects) {
3845+
if (__DEV__) {
3846+
invokeLayoutEffectsUnmountInDEV(fiber);
3847+
invokeLayoutEffectsMountInDEV(fiber);
3848+
3849+
if (hasPassiveEffects) {
3850+
invokePassiveEffectsUnmountInDEV(fiber);
3851+
invokePassiveEffectsMountInDEV(fiber);
3852+
}
3853+
}
3854+
}
3855+
3856+
function invokeLayoutEffectsUnmountInDEV(firstChild) {
3857+
// unmount layout effects
3858+
let fiber = firstChild;
3859+
while (fiber !== null) {
3860+
if (fiber.child !== null) {
3861+
// Should we add a separate subtree tag for this?
3862+
const primarySubtreeTag = fiber.subtreeTag & LayoutSubtreeTag;
3863+
if (primarySubtreeTag !== NoSubtreeTag) {
3864+
invokeLayoutEffectsUnmountInDEV(fiber.child);
3865+
}
3866+
}
3867+
3868+
const effectTag = fiber.effectTag;
3869+
const current = fiber.alternate;
3870+
// This is a mount
3871+
if (current === null) {
3872+
if (effectTag & Update) {
3873+
switch (fiber.tag) {
3874+
case FunctionComponent:
3875+
case ForwardRef:
3876+
case SimpleMemoComponent:
3877+
case Block: {
3878+
invokeGuardedCallback(
3879+
null,
3880+
commitHookEffectListUnmount,
3881+
null,
3882+
HookLayout | HookHasEffect,
3883+
fiber,
3884+
);
3885+
if (hasCaughtError()) {
3886+
const unmountError = clearCaughtError();
3887+
captureCommitPhaseError(fiber, unmountError);
3888+
}
3889+
break;
3890+
}
3891+
case ClassComponent: {
3892+
const instance = fiber.stateNode;
3893+
if (typeof instance.componentWillUnmount === 'function') {
3894+
safelyCallComponentWillUnmount(fiber, instance);
3895+
}
3896+
break;
3897+
}
3898+
}
3899+
}
3900+
}
3901+
fiber = fiber.sibling;
3902+
}
3903+
}
3904+
3905+
function invokeLayoutEffectsMountInDEV(firstChild) {
3906+
// mount layout effects
3907+
if (__DEV__) {
3908+
let fiber = firstChild;
3909+
while (fiber !== null) {
3910+
if (fiber.child !== null) {
3911+
// Should we add a separate subtree tag for this?
3912+
const primarySubtreeTag = fiber.subtreeTag & LayoutSubtreeTag;
3913+
if (primarySubtreeTag !== NoSubtreeTag) {
3914+
invokeLayoutEffectsMountInDEV(fiber.child);
3915+
}
3916+
}
3917+
3918+
const effectTag = fiber.effectTag;
3919+
const current = fiber.alternate;
3920+
if (current === null) {
3921+
if (effectTag & Update) {
3922+
switch (fiber.tag) {
3923+
case FunctionComponent:
3924+
case ForwardRef:
3925+
case SimpleMemoComponent:
3926+
case Block: {
3927+
invokeGuardedCallback(
3928+
null,
3929+
commitHookEffectListMount,
3930+
null,
3931+
HookLayout | HookHasEffect,
3932+
fiber,
3933+
);
3934+
if (hasCaughtError()) {
3935+
const mountError = clearCaughtError();
3936+
captureCommitPhaseError(fiber, mountError);
3937+
}
3938+
break;
3939+
}
3940+
case ClassComponent: {
3941+
const instance = fiber.stateNode;
3942+
instance.componentDidMount();
3943+
break;
3944+
}
3945+
}
3946+
}
3947+
}
3948+
fiber = fiber.sibling;
3949+
}
3950+
}
3951+
}
3952+
3953+
function invokePassiveEffectsUnmountInDEV(firstChild): void {
3954+
if (__DEV__) {
3955+
let fiber = firstChild;
3956+
while (fiber !== null) {
3957+
if (fiber.child !== null) {
3958+
const primarySubtreeTag = fiber.subtreeTag & PassiveSubtreeTag;
3959+
if (primarySubtreeTag !== NoSubtreeTag) {
3960+
invokePassiveEffectsUnmountInDEV(fiber.child);
3961+
}
3962+
}
3963+
3964+
const current = fiber.alternate;
3965+
if (current === null) {
3966+
switch (fiber.tag) {
3967+
case FunctionComponent:
3968+
case ForwardRef:
3969+
case SimpleMemoComponent:
3970+
case Block: {
3971+
if (fiber.effectTag & Passive) {
3972+
invokeGuardedCallback(
3973+
null,
3974+
commitHookEffectListUnmount,
3975+
null,
3976+
HookPassive | HookHasEffect,
3977+
fiber,
3978+
);
3979+
if (hasCaughtError()) {
3980+
const unmountError = clearCaughtError();
3981+
captureCommitPhaseError(fiber, unmountError);
3982+
}
3983+
}
3984+
break;
3985+
}
3986+
}
3987+
}
3988+
fiber = fiber.sibling;
3989+
}
3990+
}
3991+
}
3992+
3993+
function invokePassiveEffectsMountInDEV(firstChild): void {
3994+
if (__DEV__) {
3995+
let fiber = firstChild;
3996+
while (fiber !== null) {
3997+
if (fiber.child !== null) {
3998+
const primarySubtreeTag = fiber.subtreeTag & PassiveSubtreeTag;
3999+
if (primarySubtreeTag !== NoSubtreeTag) {
4000+
invokePassiveEffectsMountInDEV(fiber.child);
4001+
}
4002+
}
4003+
4004+
const current = fiber.alternate;
4005+
if (current === null) {
4006+
switch (fiber.tag) {
4007+
case FunctionComponent:
4008+
case ForwardRef:
4009+
case SimpleMemoComponent:
4010+
case Block: {
4011+
if (fiber.effectTag & Passive) {
4012+
invokeGuardedCallback(
4013+
null,
4014+
commitHookEffectListMount,
4015+
null,
4016+
HookPassive | HookHasEffect,
4017+
fiber,
4018+
);
4019+
if (hasCaughtError()) {
4020+
const mountError = clearCaughtError();
4021+
captureCommitPhaseError(fiber, mountError);
4022+
}
4023+
}
4024+
break;
4025+
}
4026+
}
4027+
}
4028+
fiber = fiber.sibling;
4029+
}
4030+
}
4031+
}
4032+
38254033
// `act` testing API
38264034
//
38274035
// TODO: This is mostly a copy-paste from the legacy `act`, which does not have

0 commit comments

Comments
 (0)