Skip to content

Commit 1347bda

Browse files
committed
Use Visibility flag to schedule a hide/show effect
Instead of the Update flag, which is also used for other side-effects, like refs. I originally added the Visibility flag for this purpose in facebook#20043 but it got reverted last winter when we were bisecting the effects refactor.
1 parent 9090257 commit 1347bda

File tree

3 files changed

+69
-50
lines changed

3 files changed

+69
-50
lines changed

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

+41-30
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import {
7575
MutationMask,
7676
LayoutMask,
7777
PassiveMask,
78+
Visibility,
7879
} from './ReactFiberFlags';
7980
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
8081
import invariant from 'shared/invariant';
@@ -615,7 +616,7 @@ function commitLayoutEffectOnFiber(
615616
finishedWork: Fiber,
616617
committedLanes: Lanes,
617618
): void {
618-
if ((finishedWork.flags & (Update | Callback)) !== NoFlags) {
619+
if ((finishedWork.flags & LayoutMask) !== NoFlags) {
619620
switch (finishedWork.tag) {
620621
case FunctionComponent:
621622
case ForwardRef:
@@ -1776,7 +1777,7 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
17761777
return;
17771778
}
17781779
case SuspenseComponent: {
1779-
commitSuspenseComponent(finishedWork);
1780+
commitSuspenseCallback(finishedWork);
17801781
attachSuspenseRetryListeners(finishedWork);
17811782
return;
17821783
}
@@ -1899,7 +1900,7 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
18991900
return;
19001901
}
19011902
case SuspenseComponent: {
1902-
commitSuspenseComponent(finishedWork);
1903+
commitSuspenseCallback(finishedWork);
19031904
attachSuspenseRetryListeners(finishedWork);
19041905
return;
19051906
}
@@ -1918,13 +1919,6 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
19181919
}
19191920
break;
19201921
}
1921-
case OffscreenComponent:
1922-
case LegacyHiddenComponent: {
1923-
const newState: OffscreenState | null = finishedWork.memoizedState;
1924-
const isHidden = newState !== null;
1925-
hideOrUnhideAllChildren(finishedWork, isHidden);
1926-
return;
1927-
}
19281922
}
19291923
invariant(
19301924
false,
@@ -1933,27 +1927,9 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
19331927
);
19341928
}
19351929

1936-
function commitSuspenseComponent(finishedWork: Fiber) {
1930+
function commitSuspenseCallback(finishedWork: Fiber) {
1931+
// TODO: Move this to passive phase
19371932
const newState: SuspenseState | null = finishedWork.memoizedState;
1938-
1939-
if (newState !== null) {
1940-
markCommitTimeOfFallback();
1941-
1942-
if (supportsMutation) {
1943-
// Hide the Offscreen component that contains the primary children. TODO:
1944-
// Ideally, this effect would have been scheduled on the Offscreen fiber
1945-
// itself. That's how unhiding works: the Offscreen component schedules an
1946-
// effect on itself. However, in this case, the component didn't complete,
1947-
// so the fiber was never added to the effect list in the normal path. We
1948-
// could have appended it to the effect list in the Suspense component's
1949-
// second pass, but doing it this way is less complicated. This would be
1950-
// simpler if we got rid of the effect list and traversed the tree, like
1951-
// we're planning to do.
1952-
const primaryChildParent: Fiber = (finishedWork.child: any);
1953-
hideOrUnhideAllChildren(primaryChildParent, true);
1954-
}
1955-
}
1956-
19571933
if (enableSuspenseCallback && newState !== null) {
19581934
const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;
19591935
if (typeof suspenseCallback === 'function') {
@@ -2127,6 +2103,10 @@ function commitMutationEffects_complete(root: FiberRoot) {
21272103
}
21282104

21292105
function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
2106+
// TODO: The factoring of this phase function could probably be improved. Consider
2107+
// switching on the type of work before checking the flags. That's what
2108+
// we do in all the other phases. I think this one is only different
2109+
// because of the shared reconcilation logic below.
21302110
const flags = finishedWork.flags;
21312111

21322112
if (flags & ContentReset) {
@@ -2147,6 +2127,37 @@ function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
21472127
}
21482128
}
21492129

2130+
if (flags & Visibility) {
2131+
switch (finishedWork.tag) {
2132+
case SuspenseComponent: {
2133+
const newState: OffscreenState | null = finishedWork.memoizedState;
2134+
if (newState !== null) {
2135+
markCommitTimeOfFallback();
2136+
// Hide the Offscreen component that contains the primary children.
2137+
// TODO: Ideally, this effect would have been scheduled on the
2138+
// Offscreen fiber itself. That's how unhiding works: the Offscreen
2139+
// component schedules an effect on itself. However, in this case, the
2140+
// component didn't complete, so the fiber was never added to the
2141+
// effect list in the normal path. We could have appended it to the
2142+
// effect list in the Suspense component's second pass, but doing it
2143+
// this way is less complicated. This would be simpler if we got rid
2144+
// of the effect list and traversed the tree, like we're planning to
2145+
// do.
2146+
const primaryChildParent: Fiber = (finishedWork.child: any);
2147+
hideOrUnhideAllChildren(primaryChildParent, true);
2148+
}
2149+
break;
2150+
}
2151+
case OffscreenComponent:
2152+
case LegacyHiddenComponent: {
2153+
const newState: OffscreenState | null = finishedWork.memoizedState;
2154+
const isHidden = newState !== null;
2155+
hideOrUnhideAllChildren(finishedWork, isHidden);
2156+
break;
2157+
}
2158+
}
2159+
}
2160+
21502161
// The following switch statement is only concerned about placement,
21512162
// updates, and deletions. To avoid needing to add a case for every possible
21522163
// bitmap value, we remove the secondary effects from the effect tag and

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

+27-19
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99

1010
import type {Fiber} from './ReactInternalTypes';
1111
import type {Lanes, Lane} from './ReactFiberLane.new';
12-
import type {ReactScopeInstance, ReactContext} from 'shared/ReactTypes';
12+
import type {
13+
ReactScopeInstance,
14+
ReactContext,
15+
Wakeable,
16+
} from 'shared/ReactTypes';
1317
import type {FiberRoot} from './ReactInternalTypes';
1418
import type {
1519
Instance,
@@ -60,6 +64,7 @@ import {
6064
Ref,
6165
RefStatic,
6266
Update,
67+
Visibility,
6368
NoFlags,
6469
DidCapture,
6570
Snapshot,
@@ -1084,32 +1089,35 @@ function completeWork(
10841089
}
10851090
}
10861091

1087-
if (supportsPersistence) {
1088-
// TODO: Only schedule updates if not prevDidTimeout.
1089-
if (nextDidTimeout) {
1090-
// If this boundary just timed out, schedule an effect to attach a
1091-
// retry listener to the promise. This flag is also used to hide the
1092-
// primary children.
1093-
workInProgress.flags |= Update;
1094-
}
1092+
const wakeables: Set<Wakeable> | null = (workInProgress.updateQueue: any);
1093+
if (wakeables !== null) {
1094+
// Schedule an effect to attach a retry listener to the promise.
1095+
// TODO: Move to passive phase
1096+
workInProgress.flags |= Update;
10951097
}
1096-
if (supportsMutation) {
1097-
// TODO: Only schedule updates if these values are non equal, i.e. it changed.
1098-
if (nextDidTimeout || prevDidTimeout) {
1099-
// If this boundary just timed out, schedule an effect to attach a
1100-
// retry listener to the promise. This flag is also used to hide the
1101-
// primary children. In mutation mode, we also need the flag to
1102-
// *unhide* children that were previously hidden, so check if this
1103-
// is currently timed out, too.
1104-
workInProgress.flags |= Update;
1098+
1099+
if (nextDidTimeout !== prevDidTimeout) {
1100+
// If the boundary was hidden or unhidden, schedule an effect to
1101+
// toggle its visibility.
1102+
if (supportsMutation) {
1103+
workInProgress.flags |= Visibility;
1104+
}
1105+
if (supportsPersistence) {
1106+
// TODO: We don't toggle the visibility (hideOrUnhideChildren) in
1107+
// persistent mode (Fabric) because it used to only change the
1108+
// visibility of the host nodes, which in persistent mode is toggled
1109+
// during the complete phase (appendChildrenToContainer). But now we
1110+
// also use it to toggle the effects. Need to implement this.
11051111
}
11061112
}
1113+
11071114
if (
11081115
enableSuspenseCallback &&
11091116
workInProgress.updateQueue !== null &&
11101117
workInProgress.memoizedProps.suspenseCallback != null
11111118
) {
11121119
// Always notify the callback
1120+
// TODO: Move to passive phase
11131121
workInProgress.flags |= Update;
11141122
}
11151123
bubbleProperties(workInProgress);
@@ -1396,7 +1404,7 @@ function completeWork(
13961404
prevIsHidden !== nextIsHidden &&
13971405
newProps.mode !== 'unstable-defer-without-hiding'
13981406
) {
1399-
workInProgress.flags |= Update;
1407+
workInProgress.flags |= Visibility;
14001408
}
14011409
}
14021410

packages/react-reconciler/src/ReactFiberFlags.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export const MutationMask =
8282
Ref |
8383
Hydrating |
8484
Visibility;
85-
export const LayoutMask = Update | Callback | Ref;
85+
export const LayoutMask = Update | Callback | Ref | Visibility;
8686

8787
// TODO: Split into PassiveMountMask and PassiveUnmountMask
8888
export const PassiveMask = Passive | ChildDeletion;

0 commit comments

Comments
 (0)