8
*/
9
10
import type {FiberRoot} from './ReactInternalTypes';
11
-import type {Lane} from './ReactFiberLane';
11
+import type {Lane, Lanes} from './ReactFiberLane';
12
import type {PriorityLevel} from 'scheduler/src/SchedulerPriorities';
13
import type {BatchConfigTransition} from './ReactFiberTracingMarkerComponent';
14
24
getNextLanes,
25
includesSyncLane,
26
markStarvedLanesAsExpired,
27
- upgradePendingLaneToSync,
27
claimNextTransitionLane,
28
+ getNextLanesToFlushSync,
29
} from './ReactFiberLane';
30
import {
31
CommitContext,
145
export function flushSyncWorkOnAllRoots() {
146
// This is allowed to be called synchronously, but the caller should check
147
// the execution context first.
148
- flushSyncWorkAcrossRoots_impl(false);
148
+ flushSyncWorkAcrossRoots_impl(NoLanes, false);
149
}
150
151
export function flushSyncWorkOnLegacyRootsOnly() {
152
// This is allowed to be called synchronously, but the caller should check
153
// the execution context first.
154
if (!disableLegacyMode) {
155
- flushSyncWorkAcrossRoots_impl(true);
155
+ flushSyncWorkAcrossRoots_impl(NoLanes, true);
156
}
157
}
158
159
-function flushSyncWorkAcrossRoots_impl(onlyLegacy: boolean) {
159
+function flushSyncWorkAcrossRoots_impl(
160
+ syncTransitionLanes: Lanes | Lane,
161
+ onlyLegacy: boolean,
162
+) {
163
if (isFlushingWork) {
164
// Prevent reentrancy.
165
// TODO: Is this overly defensive? The callers must check the execution
182
if (onlyLegacy && (disableLegacyMode || root.tag !== LegacyRoot)) {
183
// Skip non-legacy roots.
184
} else {
182
- const workInProgressRoot = getWorkInProgressRoot();
183
- const workInProgressRootRenderLanes =
184
- getWorkInProgressRootRenderLanes();
185
- const nextLanes = getNextLanes(
186
- root,
187
- root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
188
- );
189
- if (includesSyncLane(nextLanes)) {
190
- // This root has pending sync work. Flush it now.
191
- didPerformSomeWork = true;
192
- performSyncWorkOnRoot(root, nextLanes);
185
+ if (syncTransitionLanes !== NoLanes) {
186
+ const nextLanes = getNextLanesToFlushSync(root, syncTransitionLanes);
187
+ if (nextLanes !== NoLanes) {
188
+ // This root has pending sync work. Flush it now.
189
+ didPerformSomeWork = true;
190
+ performSyncWorkOnRoot(root, nextLanes);
191
+ }
192
+ } else {
193
+ const workInProgressRoot = getWorkInProgressRoot();
194
+ const workInProgressRootRenderLanes =
195
+ getWorkInProgressRootRenderLanes();
196
+ const nextLanes = getNextLanes(
197
+ root,
198
+ root === workInProgressRoot
199
+ ? workInProgressRootRenderLanes
200
+ : NoLanes,
201
+ );
202
+ if (includesSyncLane(nextLanes)) {
203
+ // This root has pending sync work. Flush it now.
204
+ didPerformSomeWork = true;
205
+ performSyncWorkOnRoot(root, nextLanes);
206
+ }
207
}
208
}
209
root = root.next;
223
// We'll recompute this as we iterate through all the roots and schedule them.
224
mightHavePendingSyncWork = false;
225
226
+ let syncTransitionLanes = NoLanes;
227
+ if (currentEventTransitionLane !== NoLane) {
228
+ if (shouldAttemptEagerTransition()) {
229
+ // A transition was scheduled during an event, but we're going to try to
230
+ // render it synchronously anyway. We do this during a popstate event to
231
+ // preserve the scroll position of the previous page.
232
+ syncTransitionLanes = currentEventTransitionLane;
233
+ }
234
+ currentEventTransitionLane = NoLane;
235
+ }
236
+
237
const currentTime = now();
238
239
let prev = null;
240
let root = firstScheduledRoot;
241
while (root !== null) {
242
const next = root.next;
218
-
219
- if (
220
- currentEventTransitionLane !== NoLane &&
221
- shouldAttemptEagerTransition()
222
- ) {
223
- // A transition was scheduled during an event, but we're going to try to
224
- // render it synchronously anyway. We do this during a popstate event to
225
- // preserve the scroll position of the previous page.
226
- upgradePendingLaneToSync(root, currentEventTransitionLane);
227
- }
228
-
243
const nextLanes = scheduleTaskForRootDuringMicrotask(root, currentTime);
244
if (nextLanes === NoLane) {
245
// This root has no more pending work. Remove it from the schedule. To
262
} else {
263
// This root still has work. Keep it in the list.
264
prev = root;
251
- if (includesSyncLane(nextLanes)) {
265
+
266
+ // This is a fast-path optimization to early exit from
267
+ // flushSyncWorkOnAllRoots if we can be certain that there is no remaining
268
+ // synchronous work to perform. Set this to true if there might be sync
269
+ // work left.
270
+ if (
271
+ // Skip the optimization if syncTransitionLanes is set
272
+ syncTransitionLanes !== NoLanes ||
273
+ // Common case: we're not treating any extra lanes as synchronous, so we
274
+ // can just check if the next lanes are sync.
275
+ includesSyncLane(nextLanes)
276
+ ) {
277
mightHavePendingSyncWork = true;
278
}
279
}
280
root = next;
281
}
282
258
- currentEventTransitionLane = NoLane;
259
-
283
// At the end of the microtask, flush any pending synchronous work. This has
284
// to come at the end, because it does actual rendering work that might throw.
262
- flushSyncWorkOnAllRoots();
285
+ flushSyncWorkAcrossRoots_impl(syncTransitionLanes, false);
286
}
287
288
function scheduleTaskForRootDuringMicrotask(