Skip to content

Commit

Permalink
offscreen double invoke effects
Browse files Browse the repository at this point in the history
  • Loading branch information
lunaruan committed Aug 22, 2020
1 parent 0386bd0 commit 0e34c6d
Show file tree
Hide file tree
Showing 14 changed files with 773 additions and 7 deletions.
89 changes: 88 additions & 1 deletion packages/react-reconciler/src/ReactFiberCommitWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
enableFundamentalAPI,
enableSuspenseCallback,
enableScopeAPI,
enableDoubleInvokingEffects,
} from 'shared/ReactFeatureFlags';
import {
FunctionComponent,
Expand Down Expand Up @@ -165,7 +166,7 @@ const callComponentWillUnmountWithTimer = function(current, instance) {
function safelyCallComponentWillUnmount(
current: Fiber,
instance: any,
nearestMountedAncestor: Fiber,
nearestMountedAncestor: Fiber | null,
) {
if (__DEV__) {
invokeGuardedCallback(
Expand Down Expand Up @@ -1947,6 +1948,90 @@ function commitPassiveLifeCycles(finishedWork: Fiber): void {
}
}

function invokeEffectMountInDEV(fiber: Fiber, tag: HookEffectTag): void {
if (enableDoubleInvokingEffects) {
if (__DEV__) {
switch (fiber.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
case Block: {
invokeGuardedCallback(
null,
commitHookEffectListMount,
null,
tag,
fiber,
);
if (hasCaughtError()) {
const mountError = clearCaughtError();
captureCommitPhaseError(fiber, fiber.return, mountError);
}
break;
}
case ClassComponent: {
if (tag & HookLayout) {
const instance = fiber.stateNode;
invokeGuardedCallback(null, instance.componentDidMount, null);
if (hasCaughtError()) {
const mountError = clearCaughtError();
captureCommitPhaseError(fiber, fiber.return, mountError);
}
}
break;
}
}
}
}
}

function invokeEffectUnmountInDEV(fiber: Fiber, tag: HookEffectTag): void {
if (enableDoubleInvokingEffects) {
if (__DEV__) {
switch (fiber.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
case Block: {
invokeGuardedCallback(
null,
commitHookEffectListUnmount,
null,
tag,
fiber,
fiber.return,
);
if (hasCaughtError()) {
const unmountError = clearCaughtError();
captureCommitPhaseError(fiber, fiber.return, unmountError);
}
break;
}
case ClassComponent: {
if (tag & HookLayout) {
const instance = fiber.stateNode;
if (typeof instance.componentWillUnmount === 'function') {
invokeGuardedCallback(
null,
safelyCallComponentWillUnmount,
null,
fiber,
instance,
fiber.return,
);
if (hasCaughtError()) {
const unmountError = clearCaughtError();
captureCommitPhaseError(fiber, fiber.return, unmountError);
}
}
}
break;
}
}
}
}
}

export {
commitBeforeMutationLifeCycles,
commitResetTextContent,
Expand All @@ -1959,4 +2044,6 @@ export {
commitPassiveUnmount,
commitPassiveWork,
commitPassiveLifeCycles,
invokeEffectMountInDEV,
invokeEffectUnmountInDEV,
};
157 changes: 157 additions & 0 deletions packages/react-reconciler/src/ReactFiberWorkLoop.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
enableSchedulingProfiler,
enableScopeAPI,
skipUnmountedBoundaries,
enableDoubleInvokingEffects,
} from 'shared/ReactFeatureFlags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import invariant from 'shared/invariant';
Expand All @@ -53,6 +54,8 @@ import {
import {
NoEffect as NoHookEffect,
Passive as HookPassive,
Layout as HookLayout,
HasEffect as HookHasEffect,
} from './ReactHookEffectTags';
import {
logCommitStarted,
Expand Down Expand Up @@ -148,6 +151,8 @@ import {
Layout as LayoutSubtreeTag,
Passive as PassiveSubtreeTag,
PassiveStatic as PassiveStaticSubtreeTag,
MountLayoutDev as MountLayoutDevSubtreeTag,
MountPassiveDev as MountPassiveDevSubtreeTag,
} from './ReactSubtreeTags';
import {
NoLanePriority,
Expand Down Expand Up @@ -212,6 +217,8 @@ import {
commitPassiveEffectDurations,
commitResetTextContent,
isSuspenseBoundaryBeingHidden,
invokeEffectMountInDEV,
invokeEffectUnmountInDEV,
} from './ReactFiberCommitWork.new';
import {enqueueUpdate} from './ReactUpdateQueue.new';
import {resetContextDependencies} from './ReactFiberNewContext.new';
Expand Down Expand Up @@ -1956,9 +1963,21 @@ function resetChildLanes(completedWork: Fiber) {
}
if ((effectTag & LayoutMask) !== NoEffect) {
subtreeTag |= LayoutSubtreeTag;

if (__DEV__) {
if (child.alternate === null) {
subtreeTag |= MountLayoutDevSubtreeTag;
}
}
}
if ((effectTag & PassiveMask) !== NoEffect) {
subtreeTag |= PassiveSubtreeTag;

if (__DEV__) {
if (child.alternate === null) {
subtreeTag |= MountPassiveDevSubtreeTag;
}
}
}
if ((effectTag & PassiveStatic) !== NoEffect) {
subtreeTag |= PassiveStaticSubtreeTag;
Expand Down Expand Up @@ -2009,9 +2028,21 @@ function resetChildLanes(completedWork: Fiber) {
}
if ((effectTag & LayoutMask) !== NoEffect) {
subtreeTag |= LayoutSubtreeTag;

if (__DEV__) {
if (child.alternate === null) {
subtreeTag |= MountLayoutDevSubtreeTag;
}
}
}
if ((effectTag & PassiveMask) !== NoEffect) {
subtreeTag |= PassiveSubtreeTag;

if (__DEV__) {
if (child.alternate === null) {
subtreeTag |= MountPassiveDevSubtreeTag;
}
}
}
if ((effectTag & PassiveStatic) !== NoEffect) {
subtreeTag |= PassiveStaticSubtreeTag;
Expand Down Expand Up @@ -2336,6 +2367,14 @@ function commitRootImpl(root, renderPriorityLevel) {
}
}

if (enableDoubleInvokingEffects) {
if (__DEV__) {
if (!rootDidHavePassiveEffects) {
commitDoubleInvokeEffectsInDEV(root.current, false);
}
}
}

if (remainingLanes === SyncLane) {
// Count the number of times the root synchronously re-renders without
// finishing. If there are too many, it indicates an infinite update loop.
Expand Down Expand Up @@ -2921,6 +2960,12 @@ function flushPassiveEffectsImpl() {
nestedPassiveUpdateCount =
rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;

if (enableDoubleInvokingEffects) {
if (__DEV__) {
commitDoubleInvokeEffectsInDEV(root.current, true);
}
}

return true;
}

Expand Down Expand Up @@ -3213,6 +3258,118 @@ function flushRenderPhaseStrictModeWarningsInDEV() {
}
}

function commitDoubleInvokeEffectsInDEV(
fiber: Fiber,
hasPassiveEffects: boolean,
) {
if (enableDoubleInvokingEffects) {
if (__DEV__) {
invokeLayoutEffectsUnmountInDEV(fiber);
if (hasPassiveEffects) {
invokePassiveEffectsUnmountInDEV(fiber);
}

invokeLayoutEffectsMountInDEV(fiber);
if (hasPassiveEffects) {
invokePassiveEffectsMountInDEV(fiber);
}
}
}
}

function invokePassiveEffectsMountInDEV(firstChild): void {
if (enableDoubleInvokingEffects) {
if (__DEV__) {
let fiber = firstChild;
while (fiber !== null) {
if (fiber.child !== null) {
const primarySubtreeTag =
fiber.subtreeTag & MountPassiveDevSubtreeTag;
if (primarySubtreeTag !== NoSubtreeTag) {
invokePassiveEffectsMountInDEV(fiber.child);
}
}

const current = fiber.alternate;
if (current === null && (fiber.effectTag & Passive) !== NoEffect) {
invokeEffectMountInDEV(fiber, HookPassive | HookHasEffect);
}
fiber = fiber.sibling;
}
}
}
}

function invokePassiveEffectsUnmountInDEV(firstChild): void {
if (enableDoubleInvokingEffects) {
if (__DEV__) {
let fiber = firstChild;
while (fiber !== null) {
if (fiber.child !== null) {
const primarySubtreeTag =
fiber.subtreeTag & MountPassiveDevSubtreeTag;
if (primarySubtreeTag !== NoSubtreeTag) {
invokePassiveEffectsUnmountInDEV(fiber.child);
}
}

const current = fiber.alternate;
if (current === null && (fiber.effectTag & Passive) !== NoEffect) {
invokeEffectUnmountInDEV(fiber, HookPassive | HookHasEffect);
}
fiber = fiber.sibling;
}
}
}
}

function invokeLayoutEffectsMountInDEV(firstChild) {
if (enableDoubleInvokingEffects) {
if (__DEV__) {
let fiber = firstChild;
while (fiber !== null) {
if (fiber.child !== null) {
const primarySubtreeTag = fiber.subtreeTag & MountLayoutDevSubtreeTag;
if (primarySubtreeTag !== NoSubtreeTag) {
invokeLayoutEffectsMountInDEV(fiber.child);
}
}

const effectTag = fiber.effectTag;
const current = fiber.alternate;
if (current === null && (effectTag & Update) !== NoEffect) {
invokeEffectMountInDEV(fiber, HookLayout | HookHasEffect);
}
fiber = fiber.sibling;
}
}
}
}

function invokeLayoutEffectsUnmountInDEV(firstChild) {
if (enableDoubleInvokingEffects) {
if (__DEV__) {
let fiber = firstChild;
while (fiber !== null) {
if (fiber.child !== null) {
const primarySubtreeTag = fiber.subtreeTag & MountLayoutDevSubtreeTag;
if (primarySubtreeTag !== NoSubtreeTag) {
invokeLayoutEffectsUnmountInDEV(fiber.child);
}
}

const effectTag = fiber.effectTag;
const current = fiber.alternate;

if (current === null && (effectTag & Update) !== NoEffect) {
invokeEffectUnmountInDEV(fiber, HookLayout | HookHasEffect);
}
fiber = fiber.sibling;
}
}
}
}

let didWarnStateUpdateForNotYetMountedComponent: Set<string> | null = null;
function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {
if (__DEV__) {
Expand Down
15 changes: 9 additions & 6 deletions packages/react-reconciler/src/ReactSubtreeTags.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@

export type SubtreeTag = number;

export const NoEffect = /* */ 0b00000;
export const BeforeMutation = /* */ 0b00001;
export const Mutation = /* */ 0b00010;
export const Layout = /* */ 0b00100;
export const Passive = /* */ 0b01000;
export const PassiveStatic = /* */ 0b10000;
export const NoEffect = /* */ 0b0000000;
export const BeforeMutation = /* */ 0b0000001;
export const Mutation = /* */ 0b0000010;
export const Layout = /* */ 0b0000100;
export const Passive = /* */ 0b0001000;
export const PassiveStatic = /* */ 0b0010000;

export const MountLayoutDev = /* */ 0b0100000;
export const MountPassiveDev = /* */ 0b1000000;
Loading

0 comments on commit 0e34c6d

Please sign in to comment.