@samitouri / QOS-React-2 / commits / d37d7a4bb4

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 = [];