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

Remove LayoutStatic check from commit phase (#21249)

Brian Vaughn committed Apr 19, 2021 at 15:16 UTC b9c6a2b30ea6852da993a20e54089eda9ed4ba9b
3 files changed +166 -154
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+82 -76
@@ -81,8 +81,6 @@ import {
81 MutationMask,
82 LayoutMask,
83 PassiveMask,
84 - LayoutStatic,
85 - RefStatic,
84 } from './ReactFiberFlags';
85 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
86 import invariant from 'shared/invariant';
@@ -1027,6 +1025,9 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1025 const current = finishedWork.alternate;
1026 const wasHidden = current !== null && current.memoizedState !== null;
1027
1028 + // Only hide the top-most host nodes.
1029 + let hiddenHostSubtreeRoot = null;
1030 +
1031 if (supportsMutation) {
1032 // We only have the top Fiber that was inserted but we need to recurse down its
1033 // children to find all the terminal nodes.
@@ -1034,7 +1035,8 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1035 while (true) {
1036 if (node.tag === HostComponent) {
1037 const instance = node.stateNode;
1037 - if (isHidden) {
1038 + if (isHidden && hiddenHostSubtreeRoot === null) {
1039 + hiddenHostSubtreeRoot = node;
1040 hideInstance(instance);
1041 } else {
1042 unhideInstance(node.stateNode, node.memoizedProps);
@@ -1043,16 +1045,13 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1045 if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
1046 // This method is called during mutation; it should detach refs within a hidden subtree.
1047 // Attaching refs should be done elsewhere though (during layout).
1046 - if ((node.flags & RefStatic) !== NoFlags) {
1047 - if (isHidden) {
1048 - safelyDetachRef(node, finishedWork);
1049 - }
1048 + // TODO (Offscreen) Also check: flags & RefStatic
1049 + if (isHidden) {
1050 + safelyDetachRef(node, finishedWork);
1051 }
1052
1052 - if (
1053 - (node.subtreeFlags & (RefStatic | LayoutStatic)) !== NoFlags &&
1054 - node.child !== null
1055 - ) {
1053 + // TODO (Offscreen) Also check: subtreeFlags & (RefStatic | LayoutStatic)
1054 + if (node.child !== null) {
1055 node.child.return = node;
1056 node = node.child;
1057 continue;
@@ -1060,7 +1059,7 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1059 }
1060 } else if (node.tag === HostText) {
1061 const instance = node.stateNode;
1063 - if (isHidden) {
1062 + if (isHidden && hiddenHostSubtreeRoot === null) {
1063 hideTextInstance(instance);
1064 } else {
1065 unhideTextInstance(instance, node.memoizedProps);
@@ -1075,43 +1074,42 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1074 // Don't search any deeper. This tree should remain hidden.
1075 } else if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
1076 // When a mounted Suspense subtree gets hidden again, destroy any nested layout effects.
1078 - if ((node.flags & (RefStatic | LayoutStatic)) !== NoFlags) {
1079 - switch (node.tag) {
1080 - case FunctionComponent:
1081 - case ForwardRef:
1082 - case MemoComponent:
1083 - case SimpleMemoComponent: {
1084 - // Note that refs are attached by the useImperativeHandle() hook, not by commitAttachRef()
1085 - if (isHidden && !wasHidden) {
1086 - if (
1087 - enableProfilerTimer &&
1088 - enableProfilerCommitHooks &&
1089 - node.mode & ProfileMode
1090 - ) {
1091 - try {
1092 - startLayoutEffectTimer();
1093 - commitHookEffectListUnmount(HookLayout, node, finishedWork);
1094 - } finally {
1095 - recordLayoutEffectDuration(node);
1096 - }
1097 - } else {
1077 + // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
1078 + switch (node.tag) {
1079 + case FunctionComponent:
1080 + case ForwardRef:
1081 + case MemoComponent:
1082 + case SimpleMemoComponent: {
1083 + // Note that refs are attached by the useImperativeHandle() hook, not by commitAttachRef()
1084 + if (isHidden && !wasHidden) {
1085 + if (
1086 + enableProfilerTimer &&
1087 + enableProfilerCommitHooks &&
1088 + node.mode & ProfileMode
1089 + ) {
1090 + try {
1091 + startLayoutEffectTimer();
1092 commitHookEffectListUnmount(HookLayout, node, finishedWork);
1093 + } finally {
1094 + recordLayoutEffectDuration(node);
1095 }
1096 + } else {
1097 + commitHookEffectListUnmount(HookLayout, node, finishedWork);
1098 }
1101 - break;
1099 }
1103 - case ClassComponent: {
1104 - if (isHidden && !wasHidden) {
1105 - if ((node.flags & RefStatic) !== NoFlags) {
1106 - safelyDetachRef(node, finishedWork);
1107 - }
1108 - const instance = node.stateNode;
1109 - if (typeof instance.componentWillUnmount === 'function') {
1110 - safelyCallComponentWillUnmount(node, finishedWork, instance);
1111 - }
1100 + break;
1101 + }
1102 + case ClassComponent: {
1103 + if (isHidden && !wasHidden) {
1104 + // TODO (Offscreen) Check: flags & RefStatic
1105 + safelyDetachRef(node, finishedWork);
1106 +
1107 + const instance = node.stateNode;
1108 + if (typeof instance.componentWillUnmount === 'function') {
1109 + safelyCallComponentWillUnmount(node, finishedWork, instance);
1110 }
1113 - break;
1111 }
1112 + break;
1113 }
1114 }
1115
@@ -1133,8 +1131,18 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1131 if (node.return === null || node.return === finishedWork) {
1132 return;
1133 }
1134 +
1135 + if (hiddenHostSubtreeRoot === node) {
1136 + hiddenHostSubtreeRoot = null;
1137 + }
1138 +
1139 node = node.return;
1140 }
1141 +
1142 + if (hiddenHostSubtreeRoot === node) {
1143 + hiddenHostSubtreeRoot = null;
1144 + }
1145 +
1146 node.sibling.return = node.return;
1147 node = node.sibling;
1148 }
@@ -2378,11 +2386,9 @@ function commitLayoutEffects_begin(
2386 if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
2387 const visibilityChanged =
2388 !offscreenSubtreeIsHidden && offscreenSubtreeWasHidden;
2381 - if (
2382 - visibilityChanged &&
2383 - (fiber.subtreeFlags & LayoutStatic) !== NoFlags &&
2384 - firstChild !== null
2385 - ) {
2389 +
2390 + // TODO (Offscreen) Also check: subtreeFlags & LayoutStatic
2391 + if (visibilityChanged && firstChild !== null) {
2392 // We've just shown or hidden a Offscreen tree that contains layout effects.
2393 // We only enter this code path for subtrees that are updated,
2394 // because newly mounted ones would pass the LayoutMask check above.
@@ -2417,42 +2423,42 @@ function commitLayoutMountEffects_complete(
2423 // Inside of an Offscreen subtree that changed visibility during this commit.
2424 // If this subtree was hidden, layout effects will have already been destroyed (during mutation phase)
2425 // but if it was just shown, we need to (re)create the effects now.
2420 - if ((fiber.flags & LayoutStatic) !== NoFlags) {
2421 - switch (fiber.tag) {
2422 - case FunctionComponent:
2423 - case ForwardRef:
2424 - case SimpleMemoComponent: {
2425 - if (
2426 - enableProfilerTimer &&
2427 - enableProfilerCommitHooks &&
2428 - fiber.mode & ProfileMode
2429 - ) {
2430 - try {
2431 - startLayoutEffectTimer();
2432 - safelyCallCommitHookLayoutEffectListMount(fiber, fiber.return);
2433 - } finally {
2434 - recordLayoutEffectDuration(fiber);
2435 - }
2436 - } else {
2426 + // TODO (Offscreen) Check: flags & LayoutStatic
2427 + switch (fiber.tag) {
2428 + case FunctionComponent:
2429 + case ForwardRef:
2430 + case SimpleMemoComponent: {
2431 + if (
2432 + enableProfilerTimer &&
2433 + enableProfilerCommitHooks &&
2434 + fiber.mode & ProfileMode
2435 + ) {
2436 + try {
2437 + startLayoutEffectTimer();
2438 safelyCallCommitHookLayoutEffectListMount(fiber, fiber.return);
2439 + } finally {
2440 + recordLayoutEffectDuration(fiber);
2441 }
2439 - break;
2442 + } else {
2443 + safelyCallCommitHookLayoutEffectListMount(fiber, fiber.return);
2444 }
2441 - case ClassComponent: {
2442 - const instance = fiber.stateNode;
2445 + break;
2446 + }
2447 + case ClassComponent: {
2448 + const instance = fiber.stateNode;
2449 + if (typeof instance.componentDidMount === 'function') {
2450 safelyCallComponentDidMount(fiber, fiber.return, instance);
2444 - break;
2451 }
2452 + break;
2453 }
2454 }
2455
2449 - if ((fiber.flags & RefStatic) !== NoFlags) {
2450 - switch (fiber.tag) {
2451 - case ClassComponent:
2452 - case HostComponent:
2453 - safelyAttachRef(fiber, fiber.return);
2454 - break;
2455 - }
2456 + // TODO (Offscreen) Check flags & RefStatic
2457 + switch (fiber.tag) {
2458 + case ClassComponent:
2459 + case HostComponent:
2460 + safelyAttachRef(fiber, fiber.return);
2461 + break;
2462 }
2463 } else if ((fiber.flags & LayoutMask) !== NoFlags) {
2464 const current = fiber.alternate;
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+82 -76
@@ -81,8 +81,6 @@ import {
81 MutationMask,
82 LayoutMask,
83 PassiveMask,
84 - LayoutStatic,
85 - RefStatic,
84 } from './ReactFiberFlags';
85 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
86 import invariant from 'shared/invariant';
@@ -1027,6 +1025,9 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1025 const current = finishedWork.alternate;
1026 const wasHidden = current !== null && current.memoizedState !== null;
1027
1028 + // Only hide the top-most host nodes.
1029 + let hiddenHostSubtreeRoot = null;
1030 +
1031 if (supportsMutation) {
1032 // We only have the top Fiber that was inserted but we need to recurse down its
1033 // children to find all the terminal nodes.
@@ -1034,7 +1035,8 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1035 while (true) {
1036 if (node.tag === HostComponent) {
1037 const instance = node.stateNode;
1037 - if (isHidden) {
1038 + if (isHidden && hiddenHostSubtreeRoot === null) {
1039 + hiddenHostSubtreeRoot = node;
1040 hideInstance(instance);
1041 } else {
1042 unhideInstance(node.stateNode, node.memoizedProps);
@@ -1043,16 +1045,13 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1045 if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
1046 // This method is called during mutation; it should detach refs within a hidden subtree.
1047 // Attaching refs should be done elsewhere though (during layout).
1046 - if ((node.flags & RefStatic) !== NoFlags) {
1047 - if (isHidden) {
1048 - safelyDetachRef(node, finishedWork);
1049 - }
1048 + // TODO (Offscreen) Also check: flags & RefStatic
1049 + if (isHidden) {
1050 + safelyDetachRef(node, finishedWork);
1051 }
1052
1052 - if (
1053 - (node.subtreeFlags & (RefStatic | LayoutStatic)) !== NoFlags &&
1054 - node.child !== null
1055 - ) {
1053 + // TODO (Offscreen) Also check: subtreeFlags & (RefStatic | LayoutStatic)
1054 + if (node.child !== null) {
1055 node.child.return = node;
1056 node = node.child;
1057 continue;
@@ -1060,7 +1059,7 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1059 }
1060 } else if (node.tag === HostText) {
1061 const instance = node.stateNode;
1063 - if (isHidden) {
1062 + if (isHidden && hiddenHostSubtreeRoot === null) {
1063 hideTextInstance(instance);
1064 } else {
1065 unhideTextInstance(instance, node.memoizedProps);
@@ -1075,43 +1074,42 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1074 // Don't search any deeper. This tree should remain hidden.
1075 } else if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
1076 // When a mounted Suspense subtree gets hidden again, destroy any nested layout effects.
1078 - if ((node.flags & (RefStatic | LayoutStatic)) !== NoFlags) {
1079 - switch (node.tag) {
1080 - case FunctionComponent:
1081 - case ForwardRef:
1082 - case MemoComponent:
1083 - case SimpleMemoComponent: {
1084 - // Note that refs are attached by the useImperativeHandle() hook, not by commitAttachRef()
1085 - if (isHidden && !wasHidden) {
1086 - if (
1087 - enableProfilerTimer &&
1088 - enableProfilerCommitHooks &&
1089 - node.mode & ProfileMode
1090 - ) {
1091 - try {
1092 - startLayoutEffectTimer();
1093 - commitHookEffectListUnmount(HookLayout, node, finishedWork);
1094 - } finally {
1095 - recordLayoutEffectDuration(node);
1096 - }
1097 - } else {
1077 + // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
1078 + switch (node.tag) {
1079 + case FunctionComponent:
1080 + case ForwardRef:
1081 + case MemoComponent:
1082 + case SimpleMemoComponent: {
1083 + // Note that refs are attached by the useImperativeHandle() hook, not by commitAttachRef()
1084 + if (isHidden && !wasHidden) {
1085 + if (
1086 + enableProfilerTimer &&
1087 + enableProfilerCommitHooks &&
1088 + node.mode & ProfileMode
1089 + ) {
1090 + try {
1091 + startLayoutEffectTimer();
1092 commitHookEffectListUnmount(HookLayout, node, finishedWork);
1093 + } finally {
1094 + recordLayoutEffectDuration(node);
1095 }
1096 + } else {
1097 + commitHookEffectListUnmount(HookLayout, node, finishedWork);
1098 }
1101 - break;
1099 }
1103 - case ClassComponent: {
1104 - if (isHidden && !wasHidden) {
1105 - if ((node.flags & RefStatic) !== NoFlags) {
1106 - safelyDetachRef(node, finishedWork);
1107 - }
1108 - const instance = node.stateNode;
1109 - if (typeof instance.componentWillUnmount === 'function') {
1110 - safelyCallComponentWillUnmount(node, finishedWork, instance);
1111 - }
1100 + break;
1101 + }
1102 + case ClassComponent: {
1103 + if (isHidden && !wasHidden) {
1104 + // TODO (Offscreen) Check: flags & RefStatic
1105 + safelyDetachRef(node, finishedWork);
1106 +
1107 + const instance = node.stateNode;
1108 + if (typeof instance.componentWillUnmount === 'function') {
1109 + safelyCallComponentWillUnmount(node, finishedWork, instance);
1110 }
1113 - break;
1111 }
1112 + break;
1113 }
1114 }
1115
@@ -1133,8 +1131,18 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1131 if (node.return === null || node.return === finishedWork) {
1132 return;
1133 }
1134 +
1135 + if (hiddenHostSubtreeRoot === node) {
1136 + hiddenHostSubtreeRoot = null;
1137 + }
1138 +
1139 node = node.return;
1140 }
1141 +
1142 + if (hiddenHostSubtreeRoot === node) {
1143 + hiddenHostSubtreeRoot = null;
1144 + }
1145 +
1146 node.sibling.return = node.return;
1147 node = node.sibling;
1148 }
@@ -2378,11 +2386,9 @@ function commitLayoutEffects_begin(
2386 if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
2387 const visibilityChanged =
2388 !offscreenSubtreeIsHidden && offscreenSubtreeWasHidden;
2381 - if (
2382 - visibilityChanged &&
2383 - (fiber.subtreeFlags & LayoutStatic) !== NoFlags &&
2384 - firstChild !== null
2385 - ) {
2389 +
2390 + // TODO (Offscreen) Also check: subtreeFlags & LayoutStatic
2391 + if (visibilityChanged && firstChild !== null) {
2392 // We've just shown or hidden a Offscreen tree that contains layout effects.
2393 // We only enter this code path for subtrees that are updated,
2394 // because newly mounted ones would pass the LayoutMask check above.
@@ -2417,42 +2423,42 @@ function commitLayoutMountEffects_complete(
2423 // Inside of an Offscreen subtree that changed visibility during this commit.
2424 // If this subtree was hidden, layout effects will have already been destroyed (during mutation phase)
2425 // but if it was just shown, we need to (re)create the effects now.
2420 - if ((fiber.flags & LayoutStatic) !== NoFlags) {
2421 - switch (fiber.tag) {
2422 - case FunctionComponent:
2423 - case ForwardRef:
2424 - case SimpleMemoComponent: {
2425 - if (
2426 - enableProfilerTimer &&
2427 - enableProfilerCommitHooks &&
2428 - fiber.mode & ProfileMode
2429 - ) {
2430 - try {
2431 - startLayoutEffectTimer();
2432 - safelyCallCommitHookLayoutEffectListMount(fiber, fiber.return);
2433 - } finally {
2434 - recordLayoutEffectDuration(fiber);
2435 - }
2436 - } else {
2426 + // TODO (Offscreen) Check: flags & LayoutStatic
2427 + switch (fiber.tag) {
2428 + case FunctionComponent:
2429 + case ForwardRef:
2430 + case SimpleMemoComponent: {
2431 + if (
2432 + enableProfilerTimer &&
2433 + enableProfilerCommitHooks &&
2434 + fiber.mode & ProfileMode
2435 + ) {
2436 + try {
2437 + startLayoutEffectTimer();
2438 safelyCallCommitHookLayoutEffectListMount(fiber, fiber.return);
2439 + } finally {
2440 + recordLayoutEffectDuration(fiber);
2441 }
2439 - break;
2442 + } else {
2443 + safelyCallCommitHookLayoutEffectListMount(fiber, fiber.return);
2444 }
2441 - case ClassComponent: {
2442 - const instance = fiber.stateNode;
2445 + break;
2446 + }
2447 + case ClassComponent: {
2448 + const instance = fiber.stateNode;
2449 + if (typeof instance.componentDidMount === 'function') {
2450 safelyCallComponentDidMount(fiber, fiber.return, instance);
2444 - break;
2451 }
2452 + break;
2453 }
2454 }
2455
2449 - if ((fiber.flags & RefStatic) !== NoFlags) {
2450 - switch (fiber.tag) {
2451 - case ClassComponent:
2452 - case HostComponent:
2453 - safelyAttachRef(fiber, fiber.return);
2454 - break;
2455 - }
2456 + // TODO (Offscreen) Check flags & RefStatic
2457 + switch (fiber.tag) {
2458 + case ClassComponent:
2459 + case HostComponent:
2460 + safelyAttachRef(fiber, fiber.return);
2461 + break;
2462 }
2463 } else if ((fiber.flags & LayoutMask) !== NoFlags) {
2464 const current = fiber.alternate;
packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemantics-test.js
+2 -2
@@ -906,7 +906,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
906 ]);
907 expect(Scheduler).toFlushAndYield(['Text:Fallback create passive']);
908 expect(ReactNoop.getChildren()).toEqual([
909 - spanHidden('Outer', [spanHidden('Inner')]),
909 + spanHidden('Outer', [span('Inner')]),
910 span('Fallback'),
911 ]);
912
@@ -1023,7 +1023,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
1023 ]);
1024 expect(Scheduler).toFlushAndYield(['Text:Fallback create passive']);
1025 expect(ReactNoop.getChildren()).toEqual([
1026 - spanHidden('Outer', [spanHidden('MemoizedInner')]),
1026 + spanHidden('Outer', [span('MemoizedInner')]),
1027 span('Fallback'),
1028 ]);
1029