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