Convert passive mount phase to tree traversal
Andrew Clark committed
Dec 7, 2020 at 16:15 UTC
d37d7a4bb4d6b55e2f393827f65082c7ea529ab7
4 files changed
+248
-136
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+121
@@ -67,9 +67,15 @@ import {
67
Placement,
68
Snapshot,
69
Update,
70
+ Passive,
71
+ PassiveMask,
72
} from './ReactFiberFlags';
73
import getComponentName from 'shared/getComponentName';
74
import invariant from 'shared/invariant';
75
+import {
76
+ resetCurrentFiber as resetCurrentDebugFiberInDEV,
77
+ setCurrentFiber as setCurrentDebugFiberInDEV,
78
+} from './ReactCurrentFiber';
79
80
import {onCommitUnmount} from './ReactFiberDevToolsHook.new';
81
import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
@@ -78,6 +84,8 @@ import {
84
getCommitTime,
85
recordLayoutEffectDuration,
86
startLayoutEffectTimer,
87
+ recordPassiveEffectDuration,
88
+ startPassiveEffectTimer,
89
} from './ReactProfilerTimer.new';
90
import {ProfileMode} from './ReactTypeOfMode';
91
import {commitUpdateQueue} from './ReactUpdateQueue.new';
@@ -134,6 +142,8 @@ if (__DEV__) {
142
143
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
144
145
+let nextEffect: Fiber | null = null;
146
+
147
const callComponentWillUnmountWithTimer = function(current, instance) {
148
instance.props = current.memoizedProps;
149
instance.state = current.memoizedState;
@@ -1798,6 +1808,117 @@ function commitResetTextContent(current: Fiber) {
1808
resetTextContent(current.stateNode);
1809
}
1810
1811
+export function commitPassiveMountEffects(
1812
+ root: FiberRoot,
1813
+ firstChild: Fiber,
1814
+): void {
1815
+ nextEffect = firstChild;
1816
+ commitPassiveMountEffects_begin(firstChild, root);
1817
+}
1818
+
1819
+function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
1820
+ while (nextEffect !== null) {
1821
+ const fiber = nextEffect;
1822
+ const firstChild = fiber.child;
1823
+ if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) {
1824
+ ensureCorrectReturnPointer(firstChild, fiber);
1825
+ nextEffect = firstChild;
1826
+ } else {
1827
+ commitPassiveMountEffects_complete(subtreeRoot, root);
1828
+ }
1829
+ }
1830
+}
1831
+
1832
+function commitPassiveMountEffects_complete(
1833
+ subtreeRoot: Fiber,
1834
+ root: FiberRoot,
1835
+) {
1836
+ while (nextEffect !== null) {
1837
+ const fiber = nextEffect;
1838
+ if ((fiber.flags & Passive) !== NoFlags) {
1839
+ if (__DEV__) {
1840
+ setCurrentDebugFiberInDEV(fiber);
1841
+ invokeGuardedCallback(
1842
+ null,
1843
+ commitPassiveMountOnFiber,
1844
+ null,
1845
+ root,
1846
+ fiber,
1847
+ );
1848
+ if (hasCaughtError()) {
1849
+ const error = clearCaughtError();
1850
+ captureCommitPhaseError(fiber, error);
1851
+ }
1852
+ resetCurrentDebugFiberInDEV();
1853
+ } else {
1854
+ try {
1855
+ commitPassiveMountOnFiber(root, fiber);
1856
+ } catch (error) {
1857
+ captureCommitPhaseError(fiber, error);
1858
+ }
1859
+ }
1860
+ }
1861
+
1862
+ if (fiber === subtreeRoot) {
1863
+ nextEffect = null;
1864
+ return;
1865
+ }
1866
+
1867
+ const sibling = fiber.sibling;
1868
+ if (sibling !== null) {
1869
+ ensureCorrectReturnPointer(sibling, fiber.return);
1870
+ nextEffect = sibling;
1871
+ return;
1872
+ }
1873
+
1874
+ nextEffect = fiber.return;
1875
+ }
1876
+}
1877
+
1878
+function commitPassiveMountOnFiber(
1879
+ finishedRoot: FiberRoot,
1880
+ finishedWork: Fiber,
1881
+): void {
1882
+ switch (finishedWork.tag) {
1883
+ case FunctionComponent:
1884
+ case ForwardRef:
1885
+ case SimpleMemoComponent: {
1886
+ if (
1887
+ enableProfilerTimer &&
1888
+ enableProfilerCommitHooks &&
1889
+ finishedWork.mode & ProfileMode
1890
+ ) {
1891
+ startPassiveEffectTimer();
1892
+ try {
1893
+ commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
1894
+ } finally {
1895
+ recordPassiveEffectDuration(finishedWork);
1896
+ }
1897
+ } else {
1898
+ commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
1899
+ }
1900
+ break;
1901
+ }
1902
+ }
1903
+}
1904
+
1905
+let didWarnWrongReturnPointer = false;
1906
+function ensureCorrectReturnPointer(fiber, expectedReturnFiber) {
1907
+ if (__DEV__) {
1908
+ if (!didWarnWrongReturnPointer && fiber.return !== expectedReturnFiber) {
1909
+ didWarnWrongReturnPointer = true;
1910
+ console.error(
1911
+ 'Internal React error: Return pointer is inconsistent ' +
1912
+ 'with parent.',
1913
+ );
1914
+ }
1915
+ }
1916
+
1917
+ // TODO: Remove this assignment once we're confident that it won't break
1918
+ // anything, by checking the warning logs for the above invariant
1919
+ fiber.return = expectedReturnFiber;
1920
+}
1921
+
1922
export {
1923
commitBeforeMutationLifeCycles,
1924
commitResetTextContent,
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+121
@@ -67,9 +67,15 @@ import {
67
Placement,
68
Snapshot,
69
Update,
70
+ Passive,
71
+ PassiveMask,
72
} from './ReactFiberFlags';
73
import getComponentName from 'shared/getComponentName';
74
import invariant from 'shared/invariant';
75
+import {
76
+ resetCurrentFiber as resetCurrentDebugFiberInDEV,
77
+ setCurrentFiber as setCurrentDebugFiberInDEV,
78
+} from './ReactCurrentFiber';
79
80
import {onCommitUnmount} from './ReactFiberDevToolsHook.old';
81
import {resolveDefaultProps} from './ReactFiberLazyComponent.old';
@@ -78,6 +84,8 @@ import {
84
getCommitTime,
85
recordLayoutEffectDuration,
86
startLayoutEffectTimer,
87
+ recordPassiveEffectDuration,
88
+ startPassiveEffectTimer,
89
} from './ReactProfilerTimer.old';
90
import {ProfileMode} from './ReactTypeOfMode';
91
import {commitUpdateQueue} from './ReactUpdateQueue.old';
@@ -134,6 +142,8 @@ if (__DEV__) {
142
143
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
144
145
+let nextEffect: Fiber | null = null;
146
+
147
const callComponentWillUnmountWithTimer = function(current, instance) {
148
instance.props = current.memoizedProps;
149
instance.state = current.memoizedState;
@@ -1798,6 +1808,117 @@ function commitResetTextContent(current: Fiber) {
1808
resetTextContent(current.stateNode);
1809
}
1810
1811
+export function commitPassiveMountEffects(
1812
+ root: FiberRoot,
1813
+ firstChild: Fiber,
1814
+): void {
1815
+ nextEffect = firstChild;
1816
+ commitPassiveMountEffects_begin(firstChild, root);
1817
+}
1818
+
1819
+function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
1820
+ while (nextEffect !== null) {
1821
+ const fiber = nextEffect;
1822
+ const firstChild = fiber.child;
1823
+ if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) {
1824
+ ensureCorrectReturnPointer(firstChild, fiber);
1825
+ nextEffect = firstChild;
1826
+ } else {
1827
+ commitPassiveMountEffects_complete(subtreeRoot, root);
1828
+ }
1829
+ }
1830
+}
1831
+
1832
+function commitPassiveMountEffects_complete(
1833
+ subtreeRoot: Fiber,
1834
+ root: FiberRoot,
1835
+) {
1836
+ while (nextEffect !== null) {
1837
+ const fiber = nextEffect;
1838
+ if ((fiber.flags & Passive) !== NoFlags) {
1839
+ if (__DEV__) {
1840
+ setCurrentDebugFiberInDEV(fiber);
1841
+ invokeGuardedCallback(
1842
+ null,
1843
+ commitPassiveMountOnFiber,
1844
+ null,
1845
+ root,
1846
+ fiber,
1847
+ );
1848
+ if (hasCaughtError()) {
1849
+ const error = clearCaughtError();
1850
+ captureCommitPhaseError(fiber, error);
1851
+ }
1852
+ resetCurrentDebugFiberInDEV();
1853
+ } else {
1854
+ try {
1855
+ commitPassiveMountOnFiber(root, fiber);
1856
+ } catch (error) {
1857
+ captureCommitPhaseError(fiber, error);
1858
+ }
1859
+ }
1860
+ }
1861
+
1862
+ if (fiber === subtreeRoot) {
1863
+ nextEffect = null;
1864
+ return;
1865
+ }
1866
+
1867
+ const sibling = fiber.sibling;
1868
+ if (sibling !== null) {
1869
+ ensureCorrectReturnPointer(sibling, fiber.return);
1870
+ nextEffect = sibling;
1871
+ return;
1872
+ }
1873
+
1874
+ nextEffect = fiber.return;
1875
+ }
1876
+}
1877
+
1878
+function commitPassiveMountOnFiber(
1879
+ finishedRoot: FiberRoot,
1880
+ finishedWork: Fiber,
1881
+): void {
1882
+ switch (finishedWork.tag) {
1883
+ case FunctionComponent:
1884
+ case ForwardRef:
1885
+ case SimpleMemoComponent: {
1886
+ if (
1887
+ enableProfilerTimer &&
1888
+ enableProfilerCommitHooks &&
1889
+ finishedWork.mode & ProfileMode
1890
+ ) {
1891
+ startPassiveEffectTimer();
1892
+ try {
1893
+ commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
1894
+ } finally {
1895
+ recordPassiveEffectDuration(finishedWork);
1896
+ }
1897
+ } else {
1898
+ commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
1899
+ }
1900
+ break;
1901
+ }
1902
+ }
1903
+}
1904
+
1905
+let didWarnWrongReturnPointer = false;
1906
+function ensureCorrectReturnPointer(fiber, expectedReturnFiber) {
1907
+ if (__DEV__) {
1908
+ if (!didWarnWrongReturnPointer && fiber.return !== expectedReturnFiber) {
1909
+ didWarnWrongReturnPointer = true;
1910
+ console.error(
1911
+ 'Internal React error: Return pointer is inconsistent ' +
1912
+ 'with parent.',
1913
+ );
1914
+ }
1915
+ }
1916
+
1917
+ // TODO: Remove this assignment once we're confident that it won't break
1918
+ // anything, by checking the warning logs for the above invariant
1919
+ fiber.return = expectedReturnFiber;
1920
+}
1921
+
1922
export {
1923
commitBeforeMutationLifeCycles,
1924
commitResetTextContent,
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+3
-68
@@ -193,6 +193,7 @@ import {
193
commitPassiveEffectDurations,
194
commitResetTextContent,
195
isSuspenseBoundaryBeingHidden,
196
+ commitPassiveMountEffects,
197
} from './ReactFiberCommitWork.new';
198
import {enqueueUpdate} from './ReactUpdateQueue.new';
199
import {resetContextDependencies} from './ReactFiberNewContext.new';
@@ -339,7 +340,6 @@ let rootDoesHavePassiveEffects: boolean = false;
340
let rootWithPendingPassiveEffects: FiberRoot | null = null;
341
let pendingPassiveEffectsRenderPriority: ReactPriorityLevel = NoSchedulerPriority;
342
let pendingPassiveEffectsLanes: Lanes = NoLanes;
342
-let pendingPassiveHookEffectsMount: Array<HookEffect | Fiber> = [];
343
let pendingPassiveHookEffectsUnmount: Array<HookEffect | Fiber> = [];
344
let pendingPassiveProfilerEffects: Array<Fiber> = [];
345
@@ -2489,7 +2489,6 @@ export function enqueuePendingPassiveHookEffectMount(
2489
fiber: Fiber,
2490
effect: HookEffect,
2491
): void {
2492
- pendingPassiveHookEffectsMount.push(effect, fiber);
2492
if (!rootDoesHavePassiveEffects) {
2493
rootDoesHavePassiveEffects = true;
2494
scheduleCallback(NormalSchedulerPriority, () => {
@@ -2520,11 +2519,6 @@ export function enqueuePendingPassiveHookEffectUnmount(
2519
}
2520
}
2521
2523
-function invokePassiveEffectCreate(effect: HookEffect): void {
2524
- const create = effect.create;
2525
- effect.destroy = create();
2526
-}
2527
-
2522
function flushPassiveEffectsImpl() {
2523
if (rootWithPendingPassiveEffects === null) {
2524
return false;
@@ -2626,68 +2620,9 @@ function flushPassiveEffectsImpl() {
2620
}
2621
}
2622
// Second pass: Create new passive effects.
2629
- const mountEffects = pendingPassiveHookEffectsMount;
2630
- pendingPassiveHookEffectsMount = [];
2631
- for (let i = 0; i < mountEffects.length; i += 2) {
2632
- const effect = ((mountEffects[i]: any): HookEffect);
2633
- const fiber = ((mountEffects[i + 1]: any): Fiber);
2634
- if (__DEV__) {
2635
- setCurrentDebugFiberInDEV(fiber);
2636
- if (
2637
- enableProfilerTimer &&
2638
- enableProfilerCommitHooks &&
2639
- fiber.mode & ProfileMode
2640
- ) {
2641
- startPassiveEffectTimer();
2642
- invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
2643
- recordPassiveEffectDuration(fiber);
2644
- } else {
2645
- invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
2646
- }
2647
- if (hasCaughtError()) {
2648
- invariant(fiber !== null, 'Should be working on an effect.');
2649
- const error = clearCaughtError();
2650
- captureCommitPhaseError(fiber, error);
2651
- }
2652
- resetCurrentDebugFiberInDEV();
2653
- } else {
2654
- try {
2655
- const create = effect.create;
2656
- if (
2657
- enableProfilerTimer &&
2658
- enableProfilerCommitHooks &&
2659
- fiber.mode & ProfileMode
2660
- ) {
2661
- try {
2662
- startPassiveEffectTimer();
2663
- effect.destroy = create();
2664
- } finally {
2665
- recordPassiveEffectDuration(fiber);
2666
- }
2667
- } else {
2668
- effect.destroy = create();
2669
- }
2670
- } catch (error) {
2671
- invariant(fiber !== null, 'Should be working on an effect.');
2672
- captureCommitPhaseError(fiber, error);
2673
- }
2674
- }
2675
- }
2676
-
2677
- // Note: This currently assumes there are no passive effects on the root fiber
2678
- // because the root is not part of its own effect list.
2679
- // This could change in the future.
2680
- let effect = root.current.firstEffect;
2681
- while (effect !== null) {
2682
- const nextNextEffect = effect.nextEffect;
2683
- // Remove nextEffect pointer to assist GC
2684
- effect.nextEffect = null;
2685
- if (effect.flags & Deletion) {
2686
- detachFiberAfterEffects(effect);
2687
- }
2688
- effect = nextNextEffect;
2689
- }
2623
+ commitPassiveMountEffects(root, root.current);
2624
2625
+ // TODO: Move to commitPassiveMountEffects
2626
if (enableProfilerTimer && enableProfilerCommitHooks) {
2627
const profilerEffects = pendingPassiveProfilerEffects;
2628
pendingPassiveProfilerEffects = [];
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+3
-68
@@ -193,6 +193,7 @@ import {
193
commitPassiveEffectDurations,
194
commitResetTextContent,
195
isSuspenseBoundaryBeingHidden,
196
+ commitPassiveMountEffects,
197
} from './ReactFiberCommitWork.old';
198
import {enqueueUpdate} from './ReactUpdateQueue.old';
199
import {resetContextDependencies} from './ReactFiberNewContext.old';
@@ -339,7 +340,6 @@ let rootDoesHavePassiveEffects: boolean = false;
340
let rootWithPendingPassiveEffects: FiberRoot | null = null;
341
let pendingPassiveEffectsRenderPriority: ReactPriorityLevel = NoSchedulerPriority;
342
let pendingPassiveEffectsLanes: Lanes = NoLanes;
342
-let pendingPassiveHookEffectsMount: Array<HookEffect | Fiber> = [];
343
let pendingPassiveHookEffectsUnmount: Array<HookEffect | Fiber> = [];
344
let pendingPassiveProfilerEffects: Array<Fiber> = [];
345
@@ -2489,7 +2489,6 @@ export function enqueuePendingPassiveHookEffectMount(
2489
fiber: Fiber,
2490
effect: HookEffect,
2491
): void {
2492
- pendingPassiveHookEffectsMount.push(effect, fiber);
2492
if (!rootDoesHavePassiveEffects) {
2493
rootDoesHavePassiveEffects = true;
2494
scheduleCallback(NormalSchedulerPriority, () => {
@@ -2520,11 +2519,6 @@ export function enqueuePendingPassiveHookEffectUnmount(
2519
}
2520
}
2521
2523
-function invokePassiveEffectCreate(effect: HookEffect): void {
2524
- const create = effect.create;
2525
- effect.destroy = create();
2526
-}
2527
-
2522
function flushPassiveEffectsImpl() {
2523
if (rootWithPendingPassiveEffects === null) {
2524
return false;
@@ -2626,68 +2620,9 @@ function flushPassiveEffectsImpl() {
2620
}
2621
}
2622
// Second pass: Create new passive effects.
2629
- const mountEffects = pendingPassiveHookEffectsMount;
2630
- pendingPassiveHookEffectsMount = [];
2631
- for (let i = 0; i < mountEffects.length; i += 2) {
2632
- const effect = ((mountEffects[i]: any): HookEffect);
2633
- const fiber = ((mountEffects[i + 1]: any): Fiber);
2634
- if (__DEV__) {
2635
- setCurrentDebugFiberInDEV(fiber);
2636
- if (
2637
- enableProfilerTimer &&
2638
- enableProfilerCommitHooks &&
2639
- fiber.mode & ProfileMode
2640
- ) {
2641
- startPassiveEffectTimer();
2642
- invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
2643
- recordPassiveEffectDuration(fiber);
2644
- } else {
2645
- invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
2646
- }
2647
- if (hasCaughtError()) {
2648
- invariant(fiber !== null, 'Should be working on an effect.');
2649
- const error = clearCaughtError();
2650
- captureCommitPhaseError(fiber, error);
2651
- }
2652
- resetCurrentDebugFiberInDEV();
2653
- } else {
2654
- try {
2655
- const create = effect.create;
2656
- if (
2657
- enableProfilerTimer &&
2658
- enableProfilerCommitHooks &&
2659
- fiber.mode & ProfileMode
2660
- ) {
2661
- try {
2662
- startPassiveEffectTimer();
2663
- effect.destroy = create();
2664
- } finally {
2665
- recordPassiveEffectDuration(fiber);
2666
- }
2667
- } else {
2668
- effect.destroy = create();
2669
- }
2670
- } catch (error) {
2671
- invariant(fiber !== null, 'Should be working on an effect.');
2672
- captureCommitPhaseError(fiber, error);
2673
- }
2674
- }
2675
- }
2676
-
2677
- // Note: This currently assumes there are no passive effects on the root fiber
2678
- // because the root is not part of its own effect list.
2679
- // This could change in the future.
2680
- let effect = root.current.firstEffect;
2681
- while (effect !== null) {
2682
- const nextNextEffect = effect.nextEffect;
2683
- // Remove nextEffect pointer to assist GC
2684
- effect.nextEffect = null;
2685
- if (effect.flags & Deletion) {
2686
- detachFiberAfterEffects(effect);
2687
- }
2688
- effect = nextNextEffect;
2689
- }
2623
+ commitPassiveMountEffects(root, root.current);
2624
2625
+ // TODO: Move to commitPassiveMountEffects
2626
if (enableProfilerTimer && enableProfilerCommitHooks) {
2627
const profilerEffects = pendingPassiveProfilerEffects;
2628
pendingPassiveProfilerEffects = [];