Skip to content

Commit f8118ec

Browse files
committed
Unify use of _pendingVisibility in attach/detach
1 parent cb3fdd7 commit f8118ec

File tree

5 files changed

+41
-69
lines changed

5 files changed

+41
-69
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,12 @@ function updateOffscreenComponent(
665665
const prevState: OffscreenState | null =
666666
current !== null ? current.memoizedState : null;
667667

668+
// Offscreen stores pending changes to visibility in `_pendingVisibility`. This is
669+
// to support batching of `attach` and `detach` calls.
670+
workInProgress.stateNode._visibility &= ~OffscreenDetached;
671+
workInProgress.stateNode._visibility |=
672+
workInProgress.stateNode._pendingVisibility & OffscreenDetached;
673+
668674
markRef(current, workInProgress);
669675

670676
if (

packages/react-reconciler/src/ReactFiberBeginWork.old.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,12 @@ function updateOffscreenComponent(
665665
const prevState: OffscreenState | null =
666666
current !== null ? current.memoizedState : null;
667667

668+
// Offscreen stores pending changes to visibility in `_pendingVisibility`. This is
669+
// to support batching of `attach` and `detach` calls.
670+
workInProgress.stateNode._visibility &= ~OffscreenDetached;
671+
workInProgress.stateNode._visibility |=
672+
workInProgress.stateNode._pendingVisibility & OffscreenDetached;
673+
668674
markRef(current, workInProgress);
669675

670676
if (

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

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ import {
153153
clearSingleton,
154154
acquireSingletonInstance,
155155
releaseSingletonInstance,
156-
scheduleMicrotask,
157156
} from './ReactFiberHostConfig';
158157
import {
159158
captureCommitPhaseError,
@@ -2423,26 +2422,16 @@ export function detachOffscreenInstance(instance: OffscreenInstance): void {
24232422
);
24242423
}
24252424

2426-
if ((instance._visibility & OffscreenDetached) !== NoFlags) {
2425+
if ((instance._pendingVisibility & OffscreenDetached) !== NoFlags) {
24272426
// The instance is already detached, this is a noop.
24282427
return;
24292428
}
24302429

2431-
instance._pendingVisibility |= OffscreenDetached;
2432-
2433-
// Detaching needs to be postoned in case attach is called before next update.
2434-
scheduleMicrotask(() => {
2435-
if ((instance._pendingVisibility & OffscreenDetached) === NoFlags) {
2436-
// Attach was called. Offscreen does not need to be detached.
2437-
return;
2438-
}
2439-
2440-
instance._visibility |= OffscreenDetached;
2441-
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
2442-
if (root !== null) {
2443-
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
2444-
}
2445-
});
2430+
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
2431+
if (root !== null) {
2432+
instance._pendingVisibility |= OffscreenDetached;
2433+
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
2434+
}
24462435
}
24472436

24482437
export function attachOffscreenInstance(instance: OffscreenInstance): void {
@@ -2453,16 +2442,14 @@ export function attachOffscreenInstance(instance: OffscreenInstance): void {
24532442
);
24542443
}
24552444

2456-
instance._pendingVisibility &= ~OffscreenDetached;
2457-
2458-
if ((instance._visibility & OffscreenDetached) === NoFlags) {
2445+
if ((instance._pendingVisibility & OffscreenDetached) === NoFlags) {
24592446
// The instance is already attached, this is a noop.
24602447
return;
24612448
}
24622449

24632450
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
24642451
if (root !== null) {
2465-
instance._visibility &= ~OffscreenDetached;
2452+
instance._pendingVisibility &= ~OffscreenDetached;
24662453
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
24672454
}
24682455
}

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

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ import {
153153
clearSingleton,
154154
acquireSingletonInstance,
155155
releaseSingletonInstance,
156-
scheduleMicrotask,
157156
} from './ReactFiberHostConfig';
158157
import {
159158
captureCommitPhaseError,
@@ -2423,26 +2422,16 @@ export function detachOffscreenInstance(instance: OffscreenInstance): void {
24232422
);
24242423
}
24252424

2426-
if ((instance._visibility & OffscreenDetached) !== NoFlags) {
2425+
if ((instance._pendingVisibility & OffscreenDetached) !== NoFlags) {
24272426
// The instance is already detached, this is a noop.
24282427
return;
24292428
}
24302429

2431-
instance._pendingVisibility |= OffscreenDetached;
2432-
2433-
// Detaching needs to be postoned in case attach is called before next update.
2434-
scheduleMicrotask(() => {
2435-
if ((instance._pendingVisibility & OffscreenDetached) === NoFlags) {
2436-
// Attach was called. Offscreen does not need to be detached.
2437-
return;
2438-
}
2439-
2440-
instance._visibility |= OffscreenDetached;
2441-
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
2442-
if (root !== null) {
2443-
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
2444-
}
2445-
});
2430+
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
2431+
if (root !== null) {
2432+
instance._pendingVisibility |= OffscreenDetached;
2433+
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
2434+
}
24462435
}
24472436

24482437
export function attachOffscreenInstance(instance: OffscreenInstance): void {
@@ -2453,16 +2442,14 @@ export function attachOffscreenInstance(instance: OffscreenInstance): void {
24532442
);
24542443
}
24552444

2456-
instance._pendingVisibility &= ~OffscreenDetached;
2457-
2458-
if ((instance._visibility & OffscreenDetached) === NoFlags) {
2445+
if ((instance._pendingVisibility & OffscreenDetached) === NoFlags) {
24592446
// The instance is already attached, this is a noop.
24602447
return;
24612448
}
24622449

24632450
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
24642451
if (root !== null) {
2465-
instance._visibility &= ~OffscreenDetached;
2452+
instance._pendingVisibility &= ~OffscreenDetached;
24662453
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
24672454
}
24682455
}

packages/react-reconciler/src/__tests__/ReactOffscreen-test.js

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,43 +1663,29 @@ describe('ReactOffscreen', () => {
16631663

16641664
nextRenderTriggerDetach = true;
16651665

1666-
// Offscreen is attached. State updates from offscreen are **not defered**.
1667-
// Offscreen is detached inside useLayoutEffect;
1666+
// Offscreen is attached and gets detached inside useLayoutEffect.
1667+
// State updates from offscreen are **defered**.
16681668
await act(async () => {
16691669
updateChildState(1);
16701670
updateHighPriorityComponentState(1);
16711671
expect(Scheduler).toFlushUntilNextPaint([
16721672
'HighPriorityComponent 1',
16731673
'Child 1',
16741674
'HighPriorityComponent 2',
1675-
'Child 2',
16761675
]);
16771676
expect(root).toMatchRenderedOutput(
16781677
<>
16791678
<span prop="HighPriorityComponent 2" />
1680-
<span prop="Child 2" />
1681-
</>,
1682-
);
1683-
});
1684-
1685-
// Offscreen is detached. State updates from offscreen are **defered**.
1686-
await act(async () => {
1687-
updateChildState(3);
1688-
updateHighPriorityComponentState(3);
1689-
expect(Scheduler).toFlushUntilNextPaint(['HighPriorityComponent 3']);
1690-
expect(root).toMatchRenderedOutput(
1691-
<>
1692-
<span prop="HighPriorityComponent 3" />
1693-
<span prop="Child 2" />
1679+
<span prop="Child 1" />
16941680
</>,
16951681
);
16961682
});
16971683

1698-
expect(Scheduler).toHaveYielded(['Child 3']);
1684+
expect(Scheduler).toHaveYielded(['Child 2']);
16991685
expect(root).toMatchRenderedOutput(
17001686
<>
1701-
<span prop="HighPriorityComponent 3" />
1702-
<span prop="Child 3" />
1687+
<span prop="HighPriorityComponent 2" />
1688+
<span prop="Child 2" />
17031689
</>,
17041690
);
17051691

@@ -1708,16 +1694,16 @@ describe('ReactOffscreen', () => {
17081694
// Offscreen is detached. State updates from offscreen are **defered**.
17091695
// Offscreen is attached inside useLayoutEffect;
17101696
await act(async () => {
1711-
updateChildState(4);
1712-
updateHighPriorityComponentState(4);
1697+
updateChildState(3);
1698+
updateHighPriorityComponentState(3);
17131699
expect(Scheduler).toFlushUntilNextPaint([
1714-
'HighPriorityComponent 4',
1715-
'Child 4',
1700+
'HighPriorityComponent 3',
1701+
'Child 3',
17161702
]);
17171703
expect(root).toMatchRenderedOutput(
17181704
<>
1719-
<span prop="HighPriorityComponent 4" />
1720-
<span prop="Child 4" />
1705+
<span prop="HighPriorityComponent 3" />
1706+
<span prop="Child 3" />
17211707
</>,
17221708
);
17231709
});
@@ -2043,7 +2029,7 @@ describe('ReactOffscreen', () => {
20432029
instance.detach();
20442030
});
20452031

2046-
expect(Scheduler).toHaveYielded(['attach child', 'detach child']);
2032+
expect(Scheduler).toHaveYielded([]);
20472033
});
20482034

20492035
// @gate enableOffscreen

0 commit comments

Comments
 (0)