Skip to content

Commit b4f716a

Browse files
committed
offscreen double invoke effects
1 parent dab0854 commit b4f716a

13 files changed

+745
-1
lines changed

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

Lines changed: 211 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
enableFundamentalAPI,
3939
enableSuspenseCallback,
4040
enableScopeAPI,
41+
enableDoubleInvokingEffects,
4142
} from 'shared/ReactFeatureFlags';
4243
import {
4344
FunctionComponent,
@@ -71,6 +72,7 @@ import {
7172
Placement,
7273
Snapshot,
7374
Update,
75+
Passive,
7476
} from './ReactSideEffectTags';
7577
import getComponentName from 'shared/getComponentName';
7678
import invariant from 'shared/invariant';
@@ -132,6 +134,7 @@ import {
132134
import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.new';
133135
import {
134136
NoEffect as NoSubtreeTag,
137+
Layout as LayoutSubtreeTag,
135138
Passive as PassiveSubtreeTag,
136139
} from './ReactSubtreeTags';
137140

@@ -162,7 +165,7 @@ const callComponentWillUnmountWithTimer = function(current, instance) {
162165
};
163166

164167
// Capture errors so they don't interrupt unmounting.
165-
function safelyCallComponentWillUnmount(current, instance) {
168+
export function safelyCallComponentWillUnmount(current: Fiber, instance: any) {
166169
if (__DEV__) {
167170
invokeGuardedCallback(
168171
null,
@@ -1868,6 +1871,212 @@ function commitPassiveLifeCycles(finishedWork: Fiber): void {
18681871
}
18691872
}
18701873

1874+
function commitDoubleInvokeEffectsInDEV(
1875+
fiber: Fiber,
1876+
hasPassiveEffects: boolean,
1877+
) {
1878+
if (enableDoubleInvokingEffects) {
1879+
if (__DEV__) {
1880+
invokeLayoutEffectsUnmountInDEV(fiber);
1881+
if (hasPassiveEffects) {
1882+
invokePassiveEffectsUnmountInDEV(fiber);
1883+
}
1884+
1885+
invokeLayoutEffectsMountInDEV(fiber);
1886+
if (hasPassiveEffects) {
1887+
invokePassiveEffectsMountInDEV(fiber);
1888+
}
1889+
}
1890+
}
1891+
}
1892+
1893+
function invokeLayoutEffectsUnmountInDEV(firstChild) {
1894+
if (enableDoubleInvokingEffects) {
1895+
if (__DEV__) {
1896+
// unmount layout effects
1897+
let fiber = firstChild;
1898+
while (fiber !== null) {
1899+
if (fiber.child !== null) {
1900+
// Should we add a separate subtree tag for this?
1901+
const primarySubtreeTag = fiber.subtreeTag & LayoutSubtreeTag;
1902+
if (primarySubtreeTag !== NoSubtreeTag) {
1903+
invokeLayoutEffectsUnmountInDEV(fiber.child);
1904+
}
1905+
}
1906+
1907+
const effectTag = fiber.effectTag;
1908+
const current = fiber.alternate;
1909+
// This is a mount
1910+
if (current === null) {
1911+
if (effectTag & Update) {
1912+
switch (fiber.tag) {
1913+
case FunctionComponent:
1914+
case ForwardRef:
1915+
case SimpleMemoComponent:
1916+
case Block: {
1917+
invokeGuardedCallback(
1918+
null,
1919+
commitHookEffectListUnmount,
1920+
null,
1921+
HookLayout | HookHasEffect,
1922+
fiber,
1923+
);
1924+
if (hasCaughtError()) {
1925+
const unmountError = clearCaughtError();
1926+
captureCommitPhaseError(fiber, unmountError);
1927+
}
1928+
break;
1929+
}
1930+
case ClassComponent: {
1931+
const instance = fiber.stateNode;
1932+
if (typeof instance.componentWillUnmount === 'function') {
1933+
safelyCallComponentWillUnmount(fiber, instance);
1934+
}
1935+
break;
1936+
}
1937+
}
1938+
}
1939+
}
1940+
fiber = fiber.sibling;
1941+
}
1942+
}
1943+
}
1944+
}
1945+
1946+
function invokeLayoutEffectsMountInDEV(firstChild) {
1947+
if (enableDoubleInvokingEffects) {
1948+
// mount layout effects
1949+
if (__DEV__) {
1950+
let fiber = firstChild;
1951+
while (fiber !== null) {
1952+
if (fiber.child !== null) {
1953+
// Should we add a separate subtree tag for this?
1954+
const primarySubtreeTag = fiber.subtreeTag & LayoutSubtreeTag;
1955+
if (primarySubtreeTag !== NoSubtreeTag) {
1956+
invokeLayoutEffectsMountInDEV(fiber.child);
1957+
}
1958+
}
1959+
1960+
const effectTag = fiber.effectTag;
1961+
const current = fiber.alternate;
1962+
if (current === null) {
1963+
if (effectTag & Update) {
1964+
switch (fiber.tag) {
1965+
case FunctionComponent:
1966+
case ForwardRef:
1967+
case SimpleMemoComponent:
1968+
case Block: {
1969+
invokeGuardedCallback(
1970+
null,
1971+
commitHookEffectListMount,
1972+
null,
1973+
HookLayout | HookHasEffect,
1974+
fiber,
1975+
);
1976+
if (hasCaughtError()) {
1977+
const mountError = clearCaughtError();
1978+
captureCommitPhaseError(fiber, mountError);
1979+
}
1980+
break;
1981+
}
1982+
case ClassComponent: {
1983+
const instance = fiber.stateNode;
1984+
instance.componentDidMount();
1985+
break;
1986+
}
1987+
}
1988+
}
1989+
}
1990+
fiber = fiber.sibling;
1991+
}
1992+
}
1993+
}
1994+
}
1995+
1996+
function invokePassiveEffectsUnmountInDEV(firstChild): void {
1997+
if (enableDoubleInvokingEffects) {
1998+
if (__DEV__) {
1999+
let fiber = firstChild;
2000+
while (fiber !== null) {
2001+
if (fiber.child !== null) {
2002+
const primarySubtreeTag = fiber.subtreeTag & PassiveSubtreeTag;
2003+
if (primarySubtreeTag !== NoSubtreeTag) {
2004+
invokePassiveEffectsUnmountInDEV(fiber.child);
2005+
}
2006+
}
2007+
2008+
const current = fiber.alternate;
2009+
if (current === null) {
2010+
switch (fiber.tag) {
2011+
case FunctionComponent:
2012+
case ForwardRef:
2013+
case SimpleMemoComponent:
2014+
case Block: {
2015+
if (fiber.effectTag & Passive) {
2016+
invokeGuardedCallback(
2017+
null,
2018+
commitHookEffectListUnmount,
2019+
null,
2020+
HookPassive | HookHasEffect,
2021+
fiber,
2022+
);
2023+
if (hasCaughtError()) {
2024+
const unmountError = clearCaughtError();
2025+
captureCommitPhaseError(fiber, unmountError);
2026+
}
2027+
}
2028+
break;
2029+
}
2030+
}
2031+
}
2032+
fiber = fiber.sibling;
2033+
}
2034+
}
2035+
}
2036+
}
2037+
2038+
function invokePassiveEffectsMountInDEV(firstChild): void {
2039+
if (enableDoubleInvokingEffects) {
2040+
if (__DEV__) {
2041+
let fiber = firstChild;
2042+
while (fiber !== null) {
2043+
if (fiber.child !== null) {
2044+
const primarySubtreeTag = fiber.subtreeTag & PassiveSubtreeTag;
2045+
if (primarySubtreeTag !== NoSubtreeTag) {
2046+
invokePassiveEffectsMountInDEV(fiber.child);
2047+
}
2048+
}
2049+
2050+
const current = fiber.alternate;
2051+
if (current === null) {
2052+
switch (fiber.tag) {
2053+
case FunctionComponent:
2054+
case ForwardRef:
2055+
case SimpleMemoComponent:
2056+
case Block: {
2057+
if (fiber.effectTag & Passive) {
2058+
invokeGuardedCallback(
2059+
null,
2060+
commitHookEffectListMount,
2061+
null,
2062+
HookPassive | HookHasEffect,
2063+
fiber,
2064+
);
2065+
if (hasCaughtError()) {
2066+
const mountError = clearCaughtError();
2067+
captureCommitPhaseError(fiber, mountError);
2068+
}
2069+
}
2070+
break;
2071+
}
2072+
}
2073+
}
2074+
fiber = fiber.sibling;
2075+
}
2076+
}
2077+
}
2078+
}
2079+
18712080
export {
18722081
commitBeforeMutationLifeCycles,
18732082
commitResetTextContent,
@@ -1880,4 +2089,5 @@ export {
18802089
commitPassiveUnmount,
18812090
commitPassiveWork,
18822091
commitPassiveLifeCycles,
2092+
commitDoubleInvokeEffectsInDEV,
18832093
};

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
enableDebugTracing,
3131
enableSchedulingProfiler,
3232
enableScopeAPI,
33+
enableDoubleInvokingEffects,
3334
} from 'shared/ReactFeatureFlags';
3435
import ReactSharedInternals from 'shared/ReactSharedInternals';
3536
import invariant from 'shared/invariant';
@@ -211,6 +212,7 @@ import {
211212
commitPassiveEffectDurations,
212213
commitResetTextContent,
213214
isSuspenseBoundaryBeingHidden,
215+
commitDoubleInvokeEffectsInDEV,
214216
} from './ReactFiberCommitWork.new';
215217
import {enqueueUpdate} from './ReactUpdateQueue.new';
216218
import {resetContextDependencies} from './ReactFiberNewContext.new';
@@ -2323,6 +2325,14 @@ function commitRootImpl(root, renderPriorityLevel) {
23232325
}
23242326
}
23252327

2328+
if (enableDoubleInvokingEffects) {
2329+
if (__DEV__) {
2330+
if (!rootDidHavePassiveEffects) {
2331+
commitDoubleInvokeEffectsInDEV(root.current, false);
2332+
}
2333+
}
2334+
}
2335+
23262336
if (remainingLanes === SyncLane) {
23272337
// Count the number of times the root synchronously re-renders without
23282338
// finishing. If there are too many, it indicates an infinite update loop.
@@ -2892,6 +2902,12 @@ function flushPassiveEffectsImpl() {
28922902
nestedPassiveUpdateCount =
28932903
rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;
28942904

2905+
if (enableDoubleInvokingEffects) {
2906+
if (__DEV__) {
2907+
commitDoubleInvokeEffectsInDEV(root.current, true);
2908+
}
2909+
}
2910+
28952911
return true;
28962912
}
28972913

0 commit comments

Comments
 (0)