@samitouri / QOS-React-2 / commits / 1cd90d2ccc

Refactor of interleaved ("concurrent") update queue (#24663)

* Always push updates to interleaved queue first Interleaves updates (updates that are scheduled while another render is already is progress) go into a special queue that isn't applied until the end of the current render. They are transferred to the "real" queue at the beginning of the next render. Currently we check during `setState` whether an update should go directly onto the real queue or onto the special interleaved queue. The logic is subtle and it can lead to bugs if you mess it up, as in #24400. Instead, this changes it to always go onto the interleaved queue. The behavior is the same but the logic is simpler. As a further step, we can also wait to update the `childLanes` until the end of the current render. I'll do this in the next step. * Move setState return path traversal to own call A lot of the logic around scheduling an update needs access to the fiber root. To obtain this reference, we must walk up the fiber return path. We also do this to update `childLanes` on all the parent nodes, so we can use the same traversal for both purposes. The traversal currently happens inside `scheduleUpdateOnFiber`, but sometimes we need to access it beyond that function, too. So I've hoisted the traversal out of `scheduleUpdateOnFiber` into its own function call that happens at the beginning of the `setState` algorithm. * Rename ReactInterleavedUpdates -> ReactFiberConcurrentUpdates The scope of this module is expanding so I've renamed accordingly. No behavioral changes. * Enqueue and update childLanes in same function During a setState, the childLanes are updated immediately, even if a render is already in progress. This can lead to subtle concurrency bugs, so the plan is to wait until the in-progress render has finished before updating the childLanes, to prevent subtle concurrency bugs. As a step toward that change, when scheduling an update, we should not update the childLanes directly, but instead defer to the ReactConcurrentUpdates module to do it at the appropriate time. This makes markUpdateLaneFromFiberToRoot a private function that is only called from the ReactConcurrentUpdates module. * [FORKED] Don't update childLanes until after current render (This is the riskiest commit in the stack. Only affects the "new" reconciler fork.) Updates that occur in a concurrent event while a render is already in progress can't be processed during that render. This is tricky to get right. Previously we solved this by adding concurrent updates to a special `interleaved` queue, then transferring the `interleaved` queue to the `pending` queue after the render phase had completed. However, we would still mutate the `childLanes` along the parent path immediately, which can lead to its own subtle data races. Instead, we can queue the entire operation until after the render phase has completed. This replaces the need for an `interleaved` field on every fiber/hook queue. The main motivation for this change, aside from simplifying the logic a bit, is so we can read information about the current fiber while we're walking up its return path, like whether it's inside a hidden tree. (I haven't done anything like that in this commit, though.) * Add 17691ac to forked revisions

Andrew Clark committed Jun 6, 2022 at 12:15 UTC 1cd90d2ccc791f3ed25d93ceb7137746185f6e34
28 files changed +729 -562
packages/react-noop-renderer/src/createReactNoop.js
+1 -1
@@ -18,7 +18,7 @@ import type {
18 Fiber,
19 TransitionTracingCallbacks,
20 } from 'react-reconciler/src/ReactInternalTypes';
21 -import type {UpdateQueue} from 'react-reconciler/src/ReactUpdateQueue';
21 +import type {UpdateQueue} from 'react-reconciler/src/ReactFiberClassUpdateQueue.new';
22 import type {ReactNodeList} from 'shared/ReactTypes';
23 import type {RootTag} from 'react-reconciler/src/ReactRootTags';
24
packages/react-reconciler/src/ReactFiberBeginWork.new.js
+10 -3
@@ -33,7 +33,7 @@ import type {
33 CacheComponentState,
34 SpawnedCachePool,
35 } from './ReactFiberCacheComponent.new';
36 -import type {UpdateQueue} from './ReactUpdateQueue.new';
36 +import type {UpdateQueue} from './ReactFiberClassUpdateQueue.new';
37 import type {RootState} from './ReactFiberRoot.new';
38 import {
39 enableSuspenseAvoidThisFallback,
@@ -131,7 +131,7 @@ import {
131 cloneUpdateQueue,
132 initializeUpdateQueue,
133 enqueueCapturedUpdate,
134 -} from './ReactUpdateQueue.new';
134 +} from './ReactFiberClassUpdateQueue.new';
135 import {
136 NoLane,
137 NoLanes,
@@ -234,6 +234,7 @@ import {
234 getWorkInProgressRoot,
235 pushRenderLanes,
236 } from './ReactFiberWorkLoop.new';
237 +import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates.new';
238 import {setWorkInProgressVersion} from './ReactMutableSource.new';
239 import {pushCacheProvider, CacheContext} from './ReactFiberCacheComponent.new';
240 import {createCapturedValue} from './ReactCapturedValue';
@@ -2626,7 +2627,13 @@ function updateDehydratedSuspenseComponent(
2627 suspenseState.retryLane = attemptHydrationAtLane;
2628 // TODO: Ideally this would inherit the event time of the current render
2629 const eventTime = NoTimestamp;
2629 - scheduleUpdateOnFiber(current, attemptHydrationAtLane, eventTime);
2630 + enqueueConcurrentRenderForLane(current, attemptHydrationAtLane);
2631 + scheduleUpdateOnFiber(
2632 + root,
2633 + current,
2634 + attemptHydrationAtLane,
2635 + eventTime,
2636 + );
2637 } else {
2638 // We have already tried to ping at a higher priority than we're rendering with
2639 // so if we got here, we must have failed to hydrate at those levels. We must
packages/react-reconciler/src/ReactFiberBeginWork.old.js
+10 -3
@@ -33,7 +33,7 @@ import type {
33 CacheComponentState,
34 SpawnedCachePool,
35 } from './ReactFiberCacheComponent.old';
36 -import type {UpdateQueue} from './ReactUpdateQueue.old';
36 +import type {UpdateQueue} from './ReactFiberClassUpdateQueue.old';
37 import type {RootState} from './ReactFiberRoot.old';
38 import {
39 enableSuspenseAvoidThisFallback,
@@ -131,7 +131,7 @@ import {
131 cloneUpdateQueue,
132 initializeUpdateQueue,
133 enqueueCapturedUpdate,
134 -} from './ReactUpdateQueue.old';
134 +} from './ReactFiberClassUpdateQueue.old';
135 import {
136 NoLane,
137 NoLanes,
@@ -234,6 +234,7 @@ import {
234 getWorkInProgressRoot,
235 pushRenderLanes,
236 } from './ReactFiberWorkLoop.old';
237 +import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates.old';
238 import {setWorkInProgressVersion} from './ReactMutableSource.old';
239 import {pushCacheProvider, CacheContext} from './ReactFiberCacheComponent.old';
240 import {createCapturedValue} from './ReactCapturedValue';
@@ -2626,7 +2627,13 @@ function updateDehydratedSuspenseComponent(
2627 suspenseState.retryLane = attemptHydrationAtLane;
2628 // TODO: Ideally this would inherit the event time of the current render
2629 const eventTime = NoTimestamp;
2629 - scheduleUpdateOnFiber(current, attemptHydrationAtLane, eventTime);
2630 + enqueueConcurrentRenderForLane(current, attemptHydrationAtLane);
2631 + scheduleUpdateOnFiber(
2632 + root,
2633 + current,
2634 + attemptHydrationAtLane,
2635 + eventTime,
2636 + );
2637 } else {
2638 // We have already tried to ping at a higher priority than we're rendering with
2639 // so if we got here, we must have failed to hydrate at those levels. We must
packages/react-reconciler/src/ReactFiberClassComponent.new.js
+8 -8
@@ -9,7 +9,7 @@
9
10 import type {Fiber} from './ReactInternalTypes';
11 import type {Lanes} from './ReactFiberLane.new';
12 -import type {UpdateQueue} from './ReactUpdateQueue.new';
12 +import type {UpdateQueue} from './ReactFiberClassUpdateQueue.new';
13 import type {Flags} from './ReactFiberFlags';
14
15 import * as React from 'react';
@@ -58,7 +58,7 @@ import {
58 ForceUpdate,
59 initializeUpdateQueue,
60 cloneUpdateQueue,
61 -} from './ReactUpdateQueue.new';
61 +} from './ReactFiberClassUpdateQueue.new';
62 import {NoLanes} from './ReactFiberLane.new';
63 import {
64 cacheContext,
@@ -215,9 +215,9 @@ const classComponentUpdater = {
215 update.callback = callback;
216 }
217
218 - enqueueUpdate(fiber, update, lane);
219 - const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
218 + const root = enqueueUpdate(fiber, update, lane);
219 if (root !== null) {
220 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
221 entangleTransitions(root, fiber, lane);
222 }
223
@@ -250,9 +250,9 @@ const classComponentUpdater = {
250 update.callback = callback;
251 }
252
253 - enqueueUpdate(fiber, update, lane);
254 - const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
253 + const root = enqueueUpdate(fiber, update, lane);
254 if (root !== null) {
255 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
256 entangleTransitions(root, fiber, lane);
257 }
258
@@ -284,9 +284,9 @@ const classComponentUpdater = {
284 update.callback = callback;
285 }
286
287 - enqueueUpdate(fiber, update, lane);
288 - const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
287 + const root = enqueueUpdate(fiber, update, lane);
288 if (root !== null) {
289 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
290 entangleTransitions(root, fiber, lane);
291 }
292
packages/react-reconciler/src/ReactFiberClassComponent.old.js
+8 -8
@@ -9,7 +9,7 @@
9
10 import type {Fiber} from './ReactInternalTypes';
11 import type {Lanes} from './ReactFiberLane.old';
12 -import type {UpdateQueue} from './ReactUpdateQueue.old';
12 +import type {UpdateQueue} from './ReactFiberClassUpdateQueue.old';
13 import type {Flags} from './ReactFiberFlags';
14
15 import * as React from 'react';
@@ -58,7 +58,7 @@ import {
58 ForceUpdate,
59 initializeUpdateQueue,
60 cloneUpdateQueue,
61 -} from './ReactUpdateQueue.old';
61 +} from './ReactFiberClassUpdateQueue.old';
62 import {NoLanes} from './ReactFiberLane.old';
63 import {
64 cacheContext,
@@ -215,9 +215,9 @@ const classComponentUpdater = {
215 update.callback = callback;
216 }
217
218 - enqueueUpdate(fiber, update, lane);
219 - const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
218 + const root = enqueueUpdate(fiber, update, lane);
219 if (root !== null) {
220 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
221 entangleTransitions(root, fiber, lane);
222 }
223
@@ -250,9 +250,9 @@ const classComponentUpdater = {
250 update.callback = callback;
251 }
252
253 - enqueueUpdate(fiber, update, lane);
254 - const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
253 + const root = enqueueUpdate(fiber, update, lane);
254 if (root !== null) {
255 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
256 entangleTransitions(root, fiber, lane);
257 }
258
@@ -284,9 +284,9 @@ const classComponentUpdater = {
284 update.callback = callback;
285 }
286
287 - enqueueUpdate(fiber, update, lane);
288 - const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
287 + const root = enqueueUpdate(fiber, update, lane);
288 if (root !== null) {
289 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
290 entangleTransitions(root, fiber, lane);
291 }
292
packages/react-reconciler/src/ReactFiberClassUpdateQueue.new.js renamed
+30 -42
@@ -107,9 +107,12 @@ import {debugRenderPhaseSideEffectsForStrictMode} from 'shared/ReactFeatureFlags
107 import {StrictLegacyMode} from './ReactTypeOfMode';
108 import {
109 markSkippedUpdateLanes,
110 - isInterleavedUpdate,
110 + isUnsafeClassRenderPhaseUpdate,
111 } from './ReactFiberWorkLoop.new';
112 -import {pushInterleavedQueue} from './ReactFiberInterleavedUpdates.new';
112 +import {
113 + enqueueConcurrentClassUpdate,
114 + unsafe_markUpdateLaneFromFiberToRoot,
115 +} from './ReactFiberConcurrentUpdates.new';
116 import {setIsStrictModeForDevtools} from './ReactFiberDevToolsHook.new';
117
118 import assign from 'shared/assign';
@@ -129,7 +132,6 @@ export type Update<State> = {|
132
133 export type SharedQueue<State> = {|
134 pending: Update<State> | null,
132 - interleaved: Update<State> | null,
135 lanes: Lanes,
136 |};
137
@@ -169,7 +171,6 @@ export function initializeUpdateQueue<State>(fiber: Fiber): void {
171 lastBaseUpdate: null,
172 shared: {
173 pending: null,
172 - interleaved: null,
174 lanes: NoLanes,
175 },
176 effects: null,
@@ -214,40 +215,15 @@ export function enqueueUpdate<State>(
215 fiber: Fiber,
216 update: Update<State>,
217 lane: Lane,
217 -) {
218 +): FiberRoot | null {
219 const updateQueue = fiber.updateQueue;
220 if (updateQueue === null) {
221 // Only occurs if the fiber has been unmounted.
221 - return;
222 + return null;
223 }
224
225 const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
226
226 - if (isInterleavedUpdate(fiber, lane)) {
227 - const interleaved = sharedQueue.interleaved;
228 - if (interleaved === null) {
229 - // This is the first update. Create a circular list.
230 - update.next = update;
231 - // At the end of the current render, this queue's interleaved updates will
232 - // be transferred to the pending queue.
233 - pushInterleavedQueue(sharedQueue);
234 - } else {
235 - update.next = interleaved.next;
236 - interleaved.next = update;
237 - }
238 - sharedQueue.interleaved = update;
239 - } else {
240 - const pending = sharedQueue.pending;
241 - if (pending === null) {
242 - // This is the first update. Create a circular list.
243 - update.next = update;
244 - } else {
245 - update.next = pending.next;
246 - pending.next = update;
247 - }
248 - sharedQueue.pending = update;
249 - }
250 -
227 if (__DEV__) {
228 if (
229 currentlyProcessingQueue === sharedQueue &&
@@ -262,6 +238,28 @@ export function enqueueUpdate<State>(
238 didWarnUpdateInsideUpdate = true;
239 }
240 }
241 +
242 + if (isUnsafeClassRenderPhaseUpdate(fiber)) {
243 + // This is an unsafe render phase update. Add directly to the update
244 + // queue so we can process it immediately during the current render.
245 + const pending = sharedQueue.pending;
246 + if (pending === null) {
247 + // This is the first update. Create a circular list.
248 + update.next = update;
249 + } else {
250 + update.next = pending.next;
251 + pending.next = update;
252 + }
253 + sharedQueue.pending = update;
254 +
255 + // Update the childLanes even though we're most likely already rendering
256 + // this fiber. This is for backwards compatibility in the case where you
257 + // update a different component during render phase than the one that is
258 + // currently renderings (a pattern that is accompanied by a warning).
259 + return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
260 + } else {
261 + return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
262 + }
263 }
264
265 export function entangleTransitions(root: FiberRoot, fiber: Fiber, lane: Lane) {
@@ -622,17 +620,7 @@ export function processUpdateQueue<State>(
620 queue.firstBaseUpdate = newFirstBaseUpdate;
621 queue.lastBaseUpdate = newLastBaseUpdate;
622
625 - // Interleaved updates are stored on a separate queue. We aren't going to
626 - // process them during this render, but we do need to track which lanes
627 - // are remaining.
628 - const lastInterleaved = queue.shared.interleaved;
629 - if (lastInterleaved !== null) {
630 - let interleaved = lastInterleaved;
631 - do {
632 - newLanes = mergeLanes(newLanes, interleaved.lane);
633 - interleaved = ((interleaved: any).next: Update<State>);
634 - } while (interleaved !== lastInterleaved);
635 - } else if (firstBaseUpdate === null) {
623 + if (firstBaseUpdate === null) {
624 // `queue.lanes` is used for entangling transitions. We can set it back to
625 // zero once the queue is empty.
626 queue.shared.lanes = NoLanes;
packages/react-reconciler/src/ReactFiberClassUpdateQueue.old.js renamed
+29 -29
@@ -107,9 +107,12 @@ import {debugRenderPhaseSideEffectsForStrictMode} from 'shared/ReactFeatureFlags
107 import {StrictLegacyMode} from './ReactTypeOfMode';
108 import {
109 markSkippedUpdateLanes,
110 - isInterleavedUpdate,
110 + isUnsafeClassRenderPhaseUpdate,
111 } from './ReactFiberWorkLoop.old';
112 -import {pushInterleavedQueue} from './ReactFiberInterleavedUpdates.old';
112 +import {
113 + enqueueConcurrentClassUpdate,
114 + unsafe_markUpdateLaneFromFiberToRoot,
115 +} from './ReactFiberConcurrentUpdates.old';
116 import {setIsStrictModeForDevtools} from './ReactFiberDevToolsHook.old';
117
118 import assign from 'shared/assign';
@@ -214,40 +217,15 @@ export function enqueueUpdate<State>(
217 fiber: Fiber,
218 update: Update<State>,
219 lane: Lane,
217 -) {
220 +): FiberRoot | null {
221 const updateQueue = fiber.updateQueue;
222 if (updateQueue === null) {
223 // Only occurs if the fiber has been unmounted.
221 - return;
224 + return null;
225 }
226
227 const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
228
226 - if (isInterleavedUpdate(fiber, lane)) {
227 - const interleaved = sharedQueue.interleaved;
228 - if (interleaved === null) {
229 - // This is the first update. Create a circular list.
230 - update.next = update;
231 - // At the end of the current render, this queue's interleaved updates will
232 - // be transferred to the pending queue.
233 - pushInterleavedQueue(sharedQueue);
234 - } else {
235 - update.next = interleaved.next;
236 - interleaved.next = update;
237 - }
238 - sharedQueue.interleaved = update;
239 - } else {
240 - const pending = sharedQueue.pending;
241 - if (pending === null) {
242 - // This is the first update. Create a circular list.
243 - update.next = update;
244 - } else {
245 - update.next = pending.next;
246 - pending.next = update;
247 - }
248 - sharedQueue.pending = update;
249 - }
250 -
229 if (__DEV__) {
230 if (
231 currentlyProcessingQueue === sharedQueue &&
@@ -262,6 +240,28 @@ export function enqueueUpdate<State>(
240 didWarnUpdateInsideUpdate = true;
241 }
242 }
243 +
244 + if (isUnsafeClassRenderPhaseUpdate(fiber)) {
245 + // This is an unsafe render phase update. Add directly to the update
246 + // queue so we can process it immediately during the current render.
247 + const pending = sharedQueue.pending;
248 + if (pending === null) {
249 + // This is the first update. Create a circular list.
250 + update.next = update;
251 + } else {
252 + update.next = pending.next;
253 + pending.next = update;
254 + }
255 + sharedQueue.pending = update;
256 +
257 + // Update the childLanes even though we're most likely already rendering
258 + // this fiber. This is for backwards compatibility in the case where you
259 + // update a different component during render phase than the one that is
260 + // currently renderings (a pattern that is accompanied by a warning).
261 + return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
262 + } else {
263 + return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
264 + }
265 }
266
267 export function entangleTransitions(root: FiberRoot, fiber: Fiber, lane: Lane) {
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+2 -2
@@ -19,7 +19,7 @@ import type {Fiber} from './ReactInternalTypes';
19 import type {FiberRoot} from './ReactInternalTypes';
20 import type {Lanes} from './ReactFiberLane.new';
21 import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
22 -import type {UpdateQueue} from './ReactUpdateQueue.new';
22 +import type {UpdateQueue} from './ReactFiberClassUpdateQueue.new';
23 import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';
24 import type {Wakeable} from 'shared/ReactTypes';
25 import type {
@@ -100,7 +100,7 @@ import {
100 startPassiveEffectTimer,
101 } from './ReactProfilerTimer.new';
102 import {ConcurrentMode, NoMode, ProfileMode} from './ReactTypeOfMode';
103 -import {commitUpdateQueue} from './ReactUpdateQueue.new';
103 +import {commitUpdateQueue} from './ReactFiberClassUpdateQueue.new';
104 import {
105 getPublicInstance,
106 supportsMutation,
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+2 -2
@@ -19,7 +19,7 @@ import type {Fiber} from './ReactInternalTypes';
19 import type {FiberRoot} from './ReactInternalTypes';
20 import type {Lanes} from './ReactFiberLane.old';
21 import type {SuspenseState} from './ReactFiberSuspenseComponent.old';
22 -import type {UpdateQueue} from './ReactUpdateQueue.old';
22 +import type {UpdateQueue} from './ReactFiberClassUpdateQueue.old';
23 import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.old';
24 import type {Wakeable} from 'shared/ReactTypes';
25 import type {
@@ -100,7 +100,7 @@ import {
100 startPassiveEffectTimer,
101 } from './ReactProfilerTimer.old';
102 import {ConcurrentMode, NoMode, ProfileMode} from './ReactTypeOfMode';
103 -import {commitUpdateQueue} from './ReactUpdateQueue.old';
103 +import {commitUpdateQueue} from './ReactFiberClassUpdateQueue.old';
104 import {
105 getPublicInstance,
106 supportsMutation,
packages/react-reconciler/src/ReactFiberConcurrentUpdates.new.js new
+206
@@ -0,0 +1,206 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {FiberRoot} from './ReactInternalTypes';
11 +import type {
12 + UpdateQueue as HookQueue,
13 + Update as HookUpdate,
14 +} from './ReactFiberHooks.new';
15 +import type {
16 + SharedQueue as ClassQueue,
17 + Update as ClassUpdate,
18 +} from './ReactFiberClassUpdateQueue.new';
19 +import type {Lane, Lanes} from './ReactFiberLane.new';
20 +
21 +import {warnAboutUpdateOnNotYetMountedFiberInDEV} from './ReactFiberWorkLoop.new';
22 +import {NoLane, NoLanes, mergeLanes} from './ReactFiberLane.new';
23 +import {NoFlags, Placement, Hydrating} from './ReactFiberFlags';
24 +import {HostRoot} from './ReactWorkTags';
25 +
26 +type ConcurrentUpdate = {
27 + next: ConcurrentUpdate,
28 +};
29 +
30 +type ConcurrentQueue = {
31 + pending: ConcurrentUpdate | null,
32 +};
33 +
34 +// If a render is in progress, and we receive an update from a concurrent event,
35 +// we wait until the current render is over (either finished or interrupted)
36 +// before adding it to the fiber/hook queue. Push to this array so we can
37 +// access the queue, fiber, update, et al later.
38 +const concurrentQueues: Array<any> = [];
39 +let concurrentQueuesIndex = 0;
40 +
41 +export function finishQueueingConcurrentUpdates(): Lanes {
42 + const endIndex = concurrentQueuesIndex;
43 + concurrentQueuesIndex = 0;
44 +
45 + let lanes = NoLanes;
46 +
47 + let i = 0;
48 + while (i < endIndex) {
49 + const fiber: Fiber = concurrentQueues[i];
50 + concurrentQueues[i++] = null;
51 + const queue: ConcurrentQueue = concurrentQueues[i];
52 + concurrentQueues[i++] = null;
53 + const update: ConcurrentUpdate = concurrentQueues[i];
54 + concurrentQueues[i++] = null;
55 + const lane: Lane = concurrentQueues[i];
56 + concurrentQueues[i++] = null;
57 +
58 + if (queue !== null && update !== null) {
59 + const pending = queue.pending;
60 + if (pending === null) {
61 + // This is the first update. Create a circular list.
62 + update.next = update;
63 + } else {
64 + update.next = pending.next;
65 + pending.next = update;
66 + }
67 + queue.pending = update;
68 + }
69 +
70 + if (lane !== NoLane) {
71 + lanes = mergeLanes(lanes, lane);
72 + markUpdateLaneFromFiberToRoot(fiber, lane);
73 + }
74 + }
75 +
76 + return lanes;
77 +}
78 +
79 +function enqueueUpdate(
80 + fiber: Fiber,
81 + queue: ConcurrentQueue | null,
82 + update: ConcurrentUpdate | null,
83 + lane: Lane,
84 +) {
85 + // Don't update the `childLanes` on the return path yet. If we already in
86 + // the middle of rendering, wait until after it has completed.
87 + concurrentQueues[concurrentQueuesIndex++] = fiber;
88 + concurrentQueues[concurrentQueuesIndex++] = queue;
89 + concurrentQueues[concurrentQueuesIndex++] = update;
90 + concurrentQueues[concurrentQueuesIndex++] = lane;
91 +
92 + // The fiber's `lane` field is used in some places to check if any work is
93 + // scheduled, to perform an eager bailout, so we need to update it immediately.
94 + // TODO: We should probably move this to the "shared" queue instead.
95 + fiber.lanes = mergeLanes(fiber.lanes, lane);
96 + const alternate = fiber.alternate;
97 + if (alternate !== null) {
98 + alternate.lanes = mergeLanes(alternate.lanes, lane);
99 + }
100 +}
101 +
102 +export function enqueueConcurrentHookUpdate<S, A>(
103 + fiber: Fiber,
104 + queue: HookQueue<S, A>,
105 + update: HookUpdate<S, A>,
106 + lane: Lane,
107 +): FiberRoot | null {
108 + const concurrentQueue: ConcurrentQueue = (queue: any);
109 + const concurrentUpdate: ConcurrentUpdate = (update: any);
110 + enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane);
111 + return getRootForUpdatedFiber(fiber);
112 +}
113 +
114 +export function enqueueConcurrentHookUpdateAndEagerlyBailout<S, A>(
115 + fiber: Fiber,
116 + queue: HookQueue<S, A>,
117 + update: HookUpdate<S, A>,
118 +): void {
119 + // This function is used to queue an update that doesn't need a rerender. The
120 + // only reason we queue it is in case there's a subsequent higher priority
121 + // update that causes it to be rebased.
122 + const lane = NoLane;
123 + const concurrentQueue: ConcurrentQueue = (queue: any);
124 + const concurrentUpdate: ConcurrentUpdate = (update: any);
125 + enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane);
126 +}
127 +
128 +export function enqueueConcurrentClassUpdate<State>(
129 + fiber: Fiber,
130 + queue: ClassQueue<State>,
131 + update: ClassUpdate<State>,
132 + lane: Lane,
133 +): FiberRoot | null {
134 + const concurrentQueue: ConcurrentQueue = (queue: any);
135 + const concurrentUpdate: ConcurrentUpdate = (update: any);
136 + enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane);
137 + return getRootForUpdatedFiber(fiber);
138 +}
139 +
140 +export function enqueueConcurrentRenderForLane(
141 + fiber: Fiber,
142 + lane: Lane,
143 +): FiberRoot | null {
144 + enqueueUpdate(fiber, null, null, lane);
145 + return getRootForUpdatedFiber(fiber);
146 +}
147 +
148 +// Calling this function outside this module should only be done for backwards
149 +// compatibility and should always be accompanied by a warning.
150 +export function unsafe_markUpdateLaneFromFiberToRoot(
151 + sourceFiber: Fiber,
152 + lane: Lane,
153 +): FiberRoot | null {
154 + markUpdateLaneFromFiberToRoot(sourceFiber, lane);
155 + return getRootForUpdatedFiber(sourceFiber);
156 +}
157 +
158 +function markUpdateLaneFromFiberToRoot(sourceFiber: Fiber, lane: Lane): void {
159 + // Update the source fiber's lanes
160 + sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
161 + let alternate = sourceFiber.alternate;
162 + if (alternate !== null) {
163 + alternate.lanes = mergeLanes(alternate.lanes, lane);
164 + }
165 + // Walk the parent path to the root and update the child lanes.
166 + let parent = sourceFiber.return;
167 + while (parent !== null) {
168 + parent.childLanes = mergeLanes(parent.childLanes, lane);
169 + alternate = parent.alternate;
170 + if (alternate !== null) {
171 + alternate.childLanes = mergeLanes(alternate.childLanes, lane);
172 + }
173 + parent = parent.return;
174 + }
175 +}
176 +
177 +function getRootForUpdatedFiber(sourceFiber: Fiber): FiberRoot | null {
178 + // When a setState happens, we must ensure the root is scheduled. Because
179 + // update queues do not have a backpointer to the root, the only way to do
180 + // this currently is to walk up the return path. This used to not be a big
181 + // deal because we would have to walk up the return path to set
182 + // the `childLanes`, anyway, but now those two traversals happen at
183 + // different times.
184 + // TODO: Consider adding a `root` backpointer on the update queue.
185 + detectUpdateOnUnmountedFiber(sourceFiber, sourceFiber);
186 + let node = sourceFiber;
187 + let parent = node.return;
188 + while (parent !== null) {
189 + detectUpdateOnUnmountedFiber(sourceFiber, node);
190 + node = parent;
191 + parent = node.return;
192 + }
193 + return node.tag === HostRoot ? (node.stateNode: FiberRoot) : null;
194 +}
195 +
196 +function detectUpdateOnUnmountedFiber(sourceFiber: Fiber, parent: Fiber) {
197 + if (__DEV__) {
198 + const alternate = parent.alternate;
199 + if (
200 + alternate === null &&
201 + (parent.flags & (Placement | Hydrating)) !== NoFlags
202 + ) {
203 + warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
204 + }
205 + }
206 +}
packages/react-reconciler/src/ReactFiberConcurrentUpdates.old.js new
+184
@@ -0,0 +1,184 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {FiberRoot} from './ReactInternalTypes';
11 +import type {
12 + UpdateQueue as HookQueue,
13 + Update as HookUpdate,
14 +} from './ReactFiberHooks.old';
15 +import type {
16 + SharedQueue as ClassQueue,
17 + Update as ClassUpdate,
18 +} from './ReactFiberClassUpdateQueue.old';
19 +import type {Lane} from './ReactFiberLane.old';
20 +
21 +import {warnAboutUpdateOnNotYetMountedFiberInDEV} from './ReactFiberWorkLoop.old';
22 +import {mergeLanes} from './ReactFiberLane.old';
23 +import {NoFlags, Placement, Hydrating} from './ReactFiberFlags';
24 +import {HostRoot} from './ReactWorkTags';
25 +
26 +// An array of all update queues that received updates during the current
27 +// render. When this render exits, either because it finishes or because it is
28 +// interrupted, the interleaved updates will be transferred onto the main part
29 +// of the queue.
30 +let concurrentQueues: Array<
31 + HookQueue<any, any> | ClassQueue<any>,
32 +> | null = null;
33 +
34 +export function pushConcurrentUpdateQueue(
35 + queue: HookQueue<any, any> | ClassQueue<any>,
36 +) {
37 + if (concurrentQueues === null) {
38 + concurrentQueues = [queue];
39 + } else {
40 + concurrentQueues.push(queue);
41 + }
42 +}
43 +
44 +export function finishQueueingConcurrentUpdates() {
45 + // Transfer the interleaved updates onto the main queue. Each queue has a
46 + // `pending` field and an `interleaved` field. When they are not null, they
47 + // point to the last node in a circular linked list. We need to append the
48 + // interleaved list to the end of the pending list by joining them into a
49 + // single, circular list.
50 + if (concurrentQueues !== null) {
51 + for (let i = 0; i < concurrentQueues.length; i++) {
52 + const queue = concurrentQueues[i];
53 + const lastInterleavedUpdate = queue.interleaved;
54 + if (lastInterleavedUpdate !== null) {
55 + queue.interleaved = null;
56 + const firstInterleavedUpdate = lastInterleavedUpdate.next;
57 + const lastPendingUpdate = queue.pending;
58 + if (lastPendingUpdate !== null) {
59 + const firstPendingUpdate = lastPendingUpdate.next;
60 + lastPendingUpdate.next = (firstInterleavedUpdate: any);
61 + lastInterleavedUpdate.next = (firstPendingUpdate: any);
62 + }
63 + queue.pending = (lastInterleavedUpdate: any);
64 + }
65 + }
66 + concurrentQueues = null;
67 + }
68 +}
69 +
70 +export function enqueueConcurrentHookUpdate<S, A>(
71 + fiber: Fiber,
72 + queue: HookQueue<S, A>,
73 + update: HookUpdate<S, A>,
74 + lane: Lane,
75 +) {
76 + const interleaved = queue.interleaved;
77 + if (interleaved === null) {
78 + // This is the first update. Create a circular list.
79 + update.next = update;
80 + // At the end of the current render, this queue's interleaved updates will
81 + // be transferred to the pending queue.
82 + pushConcurrentUpdateQueue(queue);
83 + } else {
84 + update.next = interleaved.next;
85 + interleaved.next = update;
86 + }
87 + queue.interleaved = update;
88 +
89 + return markUpdateLaneFromFiberToRoot(fiber, lane);
90 +}
91 +
92 +export function enqueueConcurrentHookUpdateAndEagerlyBailout<S, A>(
93 + fiber: Fiber,
94 + queue: HookQueue<S, A>,
95 + update: HookUpdate<S, A>,
96 + lane: Lane,
97 +): void {
98 + const interleaved = queue.interleaved;
99 + if (interleaved === null) {
100 + // This is the first update. Create a circular list.
101 + update.next = update;
102 + // At the end of the current render, this queue's interleaved updates will
103 + // be transferred to the pending queue.
104 + pushConcurrentUpdateQueue(queue);
105 + } else {
106 + update.next = interleaved.next;
107 + interleaved.next = update;
108 + }
109 + queue.interleaved = update;
110 +}
111 +
112 +export function enqueueConcurrentClassUpdate<State>(
113 + fiber: Fiber,
114 + queue: ClassQueue<State>,
115 + update: ClassUpdate<State>,
116 + lane: Lane,
117 +) {
118 + const interleaved = queue.interleaved;
119 + if (interleaved === null) {
120 + // This is the first update. Create a circular list.
121 + update.next = update;
122 + // At the end of the current render, this queue's interleaved updates will
123 + // be transferred to the pending queue.
124 + pushConcurrentUpdateQueue(queue);
125 + } else {
126 + update.next = interleaved.next;
127 + interleaved.next = update;
128 + }
129 + queue.interleaved = update;
130 +
131 + return markUpdateLaneFromFiberToRoot(fiber, lane);
132 +}
133 +
134 +export function enqueueConcurrentRenderForLane(fiber: Fiber, lane: Lane) {
135 + return markUpdateLaneFromFiberToRoot(fiber, lane);
136 +}
137 +
138 +// Calling this function outside this module should only be done for backwards
139 +// compatibility and should always be accompanied by a warning.
140 +export const unsafe_markUpdateLaneFromFiberToRoot = markUpdateLaneFromFiberToRoot;
141 +
142 +function markUpdateLaneFromFiberToRoot(
143 + sourceFiber: Fiber,
144 + lane: Lane,
145 +): FiberRoot | null {
146 + // Update the source fiber's lanes
147 + sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
148 + let alternate = sourceFiber.alternate;
149 + if (alternate !== null) {
150 + alternate.lanes = mergeLanes(alternate.lanes, lane);
151 + }
152 + if (__DEV__) {
153 + if (
154 + alternate === null &&
155 + (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags
156 + ) {
157 + warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
158 + }
159 + }
160 + // Walk the parent path to the root and update the child lanes.
161 + let node = sourceFiber;
162 + let parent = sourceFiber.return;
163 + while (parent !== null) {
164 + parent.childLanes = mergeLanes(parent.childLanes, lane);
165 + alternate = parent.alternate;
166 + if (alternate !== null) {
167 + alternate.childLanes = mergeLanes(alternate.childLanes, lane);
168 + } else {
169 + if (__DEV__) {
170 + if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {
171 + warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
172 + }
173 + }
174 + }
175 + node = parent;
176 + parent = parent.return;
177 + }
178 + if (node.tag === HostRoot) {
179 + const root: FiberRoot = node.stateNode;
180 + return root;
181 + } else {
182 + return null;
183 + }
184 +}
packages/react-reconciler/src/ReactFiberHooks.new.js
+26 -68
@@ -87,7 +87,6 @@ import {
87 requestUpdateLane,
88 requestEventTime,
89 markSkippedUpdateLanes,
90 - isInterleavedUpdate,
90 } from './ReactFiberWorkLoop.new';
91
92 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
@@ -111,14 +110,18 @@ import {
110 createUpdate as createLegacyQueueUpdate,
111 enqueueUpdate as enqueueLegacyQueueUpdate,
112 entangleTransitions as entangleLegacyQueueTransitions,
114 -} from './ReactUpdateQueue.new';
115 -import {pushInterleavedQueue} from './ReactFiberInterleavedUpdates.new';
113 +} from './ReactFiberClassUpdateQueue.new';
114 +import {
115 + enqueueConcurrentHookUpdate,
116 + enqueueConcurrentHookUpdateAndEagerlyBailout,
117 + enqueueConcurrentRenderForLane,
118 +} from './ReactFiberConcurrentUpdates.new';
119 import {getTreeId} from './ReactFiberTreeContext.new';
120 import {now} from './Scheduler';
121
122 const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
123
121 -type Update<S, A> = {|
124 +export type Update<S, A> = {|
125 lane: Lane,
126 action: A,
127 hasEagerState: boolean,
@@ -128,7 +131,6 @@ type Update<S, A> = {|
131
132 export type UpdateQueue<S, A> = {|
133 pending: Update<S, A> | null,
131 - interleaved: Update<S, A> | null,
134 lanes: Lanes,
135 dispatch: (A => mixed) | null,
136 lastRenderedReducer: ((S, A) => S) | null,
@@ -738,7 +740,6 @@ function mountReducer<S, I, A>(
740 hook.memoizedState = hook.baseState = initialState;
741 const queue: UpdateQueue<S, A> = {
742 pending: null,
741 - interleaved: null,
743 lanes: NoLanes,
744 dispatch: null,
745 lastRenderedReducer: reducer,
@@ -885,22 +886,7 @@ function updateReducer<S, I, A>(
886 queue.lastRenderedState = newState;
887 }
888
888 - // Interleaved updates are stored on a separate queue. We aren't going to
889 - // process them during this render, but we do need to track which lanes
890 - // are remaining.
891 - const lastInterleaved = queue.interleaved;
892 - if (lastInterleaved !== null) {
893 - let interleaved = lastInterleaved;
894 - do {
895 - const interleavedLane = interleaved.lane;
896 - currentlyRenderingFiber.lanes = mergeLanes(
897 - currentlyRenderingFiber.lanes,
898 - interleavedLane,
899 - );
900 - markSkippedUpdateLanes(interleavedLane);
901 - interleaved = ((interleaved: any).next: Update<S, A>);
902 - } while (interleaved !== lastInterleaved);
903 - } else if (baseQueue === null) {
889 + if (baseQueue === null) {
890 // `queue.lanes` is used for entangling transitions. We can set it back to
891 // zero once the queue is empty.
892 queue.lanes = NoLanes;
@@ -1208,7 +1194,6 @@ function useMutableSource<Source, Snapshot>(
1194 // including any interleaving updates that occur.
1195 const newQueue: UpdateQueue<Snapshot, BasicStateAction<Snapshot>> = {
1196 pending: null,
1211 - interleaved: null,
1197 lanes: NoLanes,
1198 dispatch: null,
1199 lastRenderedReducer: basicStateReducer,
@@ -1497,7 +1482,10 @@ function checkIfSnapshotChanged(inst) {
1482 }
1483
1484 function forceStoreRerender(fiber) {
1500 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
1485 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
1486 + if (root !== null) {
1487 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
1488 + }
1489 }
1490
1491 function mountState<S>(
@@ -1511,7 +1499,6 @@ function mountState<S>(
1499 hook.memoizedState = hook.baseState = initialState;
1500 const queue: UpdateQueue<S, BasicStateAction<S>> = {
1501 pending: null,
1514 - interleaved: null,
1502 lanes: NoLanes,
1503 dispatch: null,
1504 lastRenderedReducer: basicStateReducer,
@@ -2153,10 +2140,13 @@ function refreshCache<T>(fiber: Fiber, seedKey: ?() => T, seedValue: T) {
2140 switch (provider.tag) {
2141 case CacheComponent:
2142 case HostRoot: {
2143 + // Schedule an update on the cache boundary to trigger a refresh.
2144 const lane = requestUpdateLane(provider);
2145 const eventTime = requestEventTime();
2158 - const root = scheduleUpdateOnFiber(provider, lane, eventTime);
2146 + const refreshUpdate = createLegacyQueueUpdate(eventTime, lane);
2147 + const root = enqueueLegacyQueueUpdate(provider, refreshUpdate, lane);
2148 if (root !== null) {
2149 + scheduleUpdateOnFiber(root, provider, lane, eventTime);
2150 entangleLegacyQueueTransitions(root, provider, lane);
2151 }
2152
@@ -2170,13 +2160,10 @@ function refreshCache<T>(fiber: Fiber, seedKey: ?() => T, seedValue: T) {
2160 seededCache.data.set(seedKey, seedValue);
2161 }
2162
2173 - // Schedule an update on the cache boundary to trigger a refresh.
2174 - const refreshUpdate = createLegacyQueueUpdate(eventTime, lane);
2163 const payload = {
2164 cache: seededCache,
2165 };
2166 refreshUpdate.payload = payload;
2179 - enqueueLegacyQueueUpdate(provider, refreshUpdate, lane);
2167 return;
2168 }
2169 }
@@ -2213,10 +2200,10 @@ function dispatchReducerAction<S, A>(
2200 if (isRenderPhaseUpdate(fiber)) {
2201 enqueueRenderPhaseUpdate(queue, update);
2202 } else {
2216 - enqueueUpdate(fiber, queue, update, lane);
2217 - const eventTime = requestEventTime();
2218 - const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
2203 + const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
2204 if (root !== null) {
2205 + const eventTime = requestEventTime();
2206 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
2207 entangleTransitionUpdate(root, queue, lane);
2208 }
2209 }
@@ -2252,8 +2239,6 @@ function dispatchSetState<S, A>(
2239 if (isRenderPhaseUpdate(fiber)) {
2240 enqueueRenderPhaseUpdate(queue, update);
2241 } else {
2255 - enqueueUpdate(fiber, queue, update, lane);
2256 -
2242 const alternate = fiber.alternate;
2243 if (
2244 fiber.lanes === NoLanes &&
@@ -2283,6 +2268,8 @@ function dispatchSetState<S, A>(
2268 // It's still possible that we'll need to rebase this update later,
2269 // if the component re-renders for a different reason and by that
2270 // time the reducer has changed.
2271 + // TODO: Do we still need to entangle transitions in this case?
2272 + enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
2273 return;
2274 }
2275 } catch (error) {
@@ -2294,9 +2281,11 @@ function dispatchSetState<S, A>(
2281 }
2282 }
2283 }
2297 - const eventTime = requestEventTime();
2298 - const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
2284 +
2285 + const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
2286 if (root !== null) {
2287 + const eventTime = requestEventTime();
2288 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
2289 entangleTransitionUpdate(root, queue, lane);
2290 }
2291 }
@@ -2331,38 +2320,7 @@ function enqueueRenderPhaseUpdate<S, A>(
2320 queue.pending = update;
2321 }
2322
2334 -function enqueueUpdate<S, A>(
2335 - fiber: Fiber,
2336 - queue: UpdateQueue<S, A>,
2337 - update: Update<S, A>,
2338 - lane: Lane,
2339 -) {
2340 - if (isInterleavedUpdate(fiber, lane)) {
2341 - const interleaved = queue.interleaved;
2342 - if (interleaved === null) {
2343 - // This is the first update. Create a circular list.
2344 - update.next = update;
2345 - // At the end of the current render, this queue's interleaved updates will
2346 - // be transferred to the pending queue.
2347 - pushInterleavedQueue(queue);
2348 - } else {
2349 - update.next = interleaved.next;
2350 - interleaved.next = update;
2351 - }
2352 - queue.interleaved = update;
2353 - } else {
2354 - const pending = queue.pending;
2355 - if (pending === null) {
2356 - // This is the first update. Create a circular list.
2357 - update.next = update;
2358 - } else {
2359 - update.next = pending.next;
2360 - pending.next = update;
2361 - }
2362 - queue.pending = update;
2363 - }
2364 -}
2365 -
2323 +// TODO: Move to ReactFiberConcurrentUpdates?
2324 function entangleTransitionUpdate<S, A>(
2325 root: FiberRoot,
2326 queue: UpdateQueue<S, A>,
packages/react-reconciler/src/ReactFiberHooks.old.js
+30 -48
@@ -87,7 +87,6 @@ import {
87 requestUpdateLane,
88 requestEventTime,
89 markSkippedUpdateLanes,
90 - isInterleavedUpdate,
90 } from './ReactFiberWorkLoop.old';
91
92 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
@@ -111,14 +110,18 @@ import {
110 createUpdate as createLegacyQueueUpdate,
111 enqueueUpdate as enqueueLegacyQueueUpdate,
112 entangleTransitions as entangleLegacyQueueTransitions,
114 -} from './ReactUpdateQueue.old';
115 -import {pushInterleavedQueue} from './ReactFiberInterleavedUpdates.old';
113 +} from './ReactFiberClassUpdateQueue.old';
114 +import {
115 + enqueueConcurrentHookUpdate,
116 + enqueueConcurrentHookUpdateAndEagerlyBailout,
117 + enqueueConcurrentRenderForLane,
118 +} from './ReactFiberConcurrentUpdates.old';
119 import {getTreeId} from './ReactFiberTreeContext.old';
120 import {now} from './Scheduler';
121
122 const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
123
121 -type Update<S, A> = {|
124 +export type Update<S, A> = {|
125 lane: Lane,
126 action: A,
127 hasEagerState: boolean,
@@ -1497,7 +1500,10 @@ function checkIfSnapshotChanged(inst) {
1500 }
1501
1502 function forceStoreRerender(fiber) {
1500 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
1503 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
1504 + if (root !== null) {
1505 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
1506 + }
1507 }
1508
1509 function mountState<S>(
@@ -2153,10 +2159,13 @@ function refreshCache<T>(fiber: Fiber, seedKey: ?() => T, seedValue: T) {
2159 switch (provider.tag) {
2160 case CacheComponent:
2161 case HostRoot: {
2162 + // Schedule an update on the cache boundary to trigger a refresh.
2163 const lane = requestUpdateLane(provider);
2164 const eventTime = requestEventTime();
2158 - const root = scheduleUpdateOnFiber(provider, lane, eventTime);
2165 + const refreshUpdate = createLegacyQueueUpdate(eventTime, lane);
2166 + const root = enqueueLegacyQueueUpdate(provider, refreshUpdate, lane);
2167 if (root !== null) {
2168 + scheduleUpdateOnFiber(root, provider, lane, eventTime);
2169 entangleLegacyQueueTransitions(root, provider, lane);
2170 }
2171
@@ -2170,13 +2179,10 @@ function refreshCache<T>(fiber: Fiber, seedKey: ?() => T, seedValue: T) {
2179 seededCache.data.set(seedKey, seedValue);
2180 }
2181
2173 - // Schedule an update on the cache boundary to trigger a refresh.
2174 - const refreshUpdate = createLegacyQueueUpdate(eventTime, lane);
2182 const payload = {
2183 cache: seededCache,
2184 };
2185 refreshUpdate.payload = payload;
2179 - enqueueLegacyQueueUpdate(provider, refreshUpdate, lane);
2186 return;
2187 }
2188 }
@@ -2213,10 +2219,10 @@ function dispatchReducerAction<S, A>(
2219 if (isRenderPhaseUpdate(fiber)) {
2220 enqueueRenderPhaseUpdate(queue, update);
2221 } else {
2216 - enqueueUpdate(fiber, queue, update, lane);
2217 - const eventTime = requestEventTime();
2218 - const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
2222 + const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
2223 if (root !== null) {
2224 + const eventTime = requestEventTime();
2225 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
2226 entangleTransitionUpdate(root, queue, lane);
2227 }
2228 }
@@ -2252,8 +2258,6 @@ function dispatchSetState<S, A>(
2258 if (isRenderPhaseUpdate(fiber)) {
2259 enqueueRenderPhaseUpdate(queue, update);
2260 } else {
2255 - enqueueUpdate(fiber, queue, update, lane);
2256 -
2261 const alternate = fiber.alternate;
2262 if (
2263 fiber.lanes === NoLanes &&
@@ -2283,6 +2287,13 @@ function dispatchSetState<S, A>(
2287 // It's still possible that we'll need to rebase this update later,
2288 // if the component re-renders for a different reason and by that
2289 // time the reducer has changed.
2290 + // TODO: Do we still need to entangle transitions in this case?
2291 + enqueueConcurrentHookUpdateAndEagerlyBailout(
2292 + fiber,
2293 + queue,
2294 + update,
2295 + lane,
2296 + );
2297 return;
2298 }
2299 } catch (error) {
@@ -2294,9 +2305,11 @@ function dispatchSetState<S, A>(
2305 }
2306 }
2307 }
2297 - const eventTime = requestEventTime();
2298 - const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
2308 +
2309 + const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
2310 if (root !== null) {
2311 + const eventTime = requestEventTime();
2312 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
2313 entangleTransitionUpdate(root, queue, lane);
2314 }
2315 }
@@ -2331,38 +2344,7 @@ function enqueueRenderPhaseUpdate<S, A>(
2344 queue.pending = update;
2345 }
2346
2334 -function enqueueUpdate<S, A>(
2335 - fiber: Fiber,
2336 - queue: UpdateQueue<S, A>,
2337 - update: Update<S, A>,
2338 - lane: Lane,
2339 -) {
2340 - if (isInterleavedUpdate(fiber, lane)) {
2341 - const interleaved = queue.interleaved;
2342 - if (interleaved === null) {
2343 - // This is the first update. Create a circular list.
2344 - update.next = update;
2345 - // At the end of the current render, this queue's interleaved updates will
2346 - // be transferred to the pending queue.
2347 - pushInterleavedQueue(queue);
2348 - } else {
2349 - update.next = interleaved.next;
2350 - interleaved.next = update;
2351 - }
2352 - queue.interleaved = update;
2353 - } else {
2354 - const pending = queue.pending;
2355 - if (pending === null) {
2356 - // This is the first update. Create a circular list.
2357 - update.next = update;
2358 - } else {
2359 - update.next = pending.next;
2360 - pending.next = update;
2361 - }
2362 - queue.pending = update;
2363 - }
2364 -}
2365 -
2347 +// TODO: Move to ReactFiberConcurrentUpdates?
2348 function entangleTransitionUpdate<S, A>(
2349 root: FiberRoot,
2350 queue: UpdateQueue<S, A>,
packages/react-reconciler/src/ReactFiberHotReloading.new.js
+5 -1
@@ -20,6 +20,7 @@ import {
20 scheduleUpdateOnFiber,
21 flushPassiveEffects,
22 } from './ReactFiberWorkLoop.new';
23 +import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates.new';
24 import {updateContainer} from './ReactFiberReconciler.new';
25 import {emptyContextObject} from './ReactFiberContext.new';
26 import {SyncLane, NoTimestamp} from './ReactFiberLane.new';
@@ -321,7 +322,10 @@ function scheduleFibersWithFamiliesRecursively(
322 fiber._debugNeedsRemount = true;
323 }
324 if (needsRemount || needsRender) {
324 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
325 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
326 + if (root !== null) {
327 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
328 + }
329 }
330 if (child !== null && !needsRemount) {
331 scheduleFibersWithFamiliesRecursively(
packages/react-reconciler/src/ReactFiberHotReloading.old.js
+5 -1
@@ -20,6 +20,7 @@ import {
20 scheduleUpdateOnFiber,
21 flushPassiveEffects,
22 } from './ReactFiberWorkLoop.old';
23 +import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates.old';
24 import {updateContainer} from './ReactFiberReconciler.old';
25 import {emptyContextObject} from './ReactFiberContext.old';
26 import {SyncLane, NoTimestamp} from './ReactFiberLane.old';
@@ -321,7 +322,10 @@ function scheduleFibersWithFamiliesRecursively(
322 fiber._debugNeedsRemount = true;
323 }
324 if (needsRemount || needsRender) {
324 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
325 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
326 + if (root !== null) {
327 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
328 + }
329 }
330 if (child !== null && !needsRemount) {
331 scheduleFibersWithFamiliesRecursively(
packages/react-reconciler/src/ReactFiberInterleavedUpdates.new.js deleted
-59
@@ -1,59 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import type {UpdateQueue as HookQueue} from './ReactFiberHooks.new';
11 -import type {SharedQueue as ClassQueue} from './ReactUpdateQueue.new';
12 -
13 -// An array of all update queues that received updates during the current
14 -// render. When this render exits, either because it finishes or because it is
15 -// interrupted, the interleaved updates will be transferred onto the main part
16 -// of the queue.
17 -let interleavedQueues: Array<
18 - HookQueue<any, any> | ClassQueue<any>,
19 -> | null = null;
20 -
21 -export function pushInterleavedQueue(
22 - queue: HookQueue<any, any> | ClassQueue<any>,
23 -) {
24 - if (interleavedQueues === null) {
25 - interleavedQueues = [queue];
26 - } else {
27 - interleavedQueues.push(queue);
28 - }
29 -}
30 -
31 -export function hasInterleavedUpdates() {
32 - return interleavedQueues !== null;
33 -}
34 -
35 -export function enqueueInterleavedUpdates() {
36 - // Transfer the interleaved updates onto the main queue. Each queue has a
37 - // `pending` field and an `interleaved` field. When they are not null, they
38 - // point to the last node in a circular linked list. We need to append the
39 - // interleaved list to the end of the pending list by joining them into a
40 - // single, circular list.
41 - if (interleavedQueues !== null) {
42 - for (let i = 0; i < interleavedQueues.length; i++) {
43 - const queue = interleavedQueues[i];
44 - const lastInterleavedUpdate = queue.interleaved;
45 - if (lastInterleavedUpdate !== null) {
46 - queue.interleaved = null;
47 - const firstInterleavedUpdate = lastInterleavedUpdate.next;
48 - const lastPendingUpdate = queue.pending;
49 - if (lastPendingUpdate !== null) {
50 - const firstPendingUpdate = lastPendingUpdate.next;
51 - lastPendingUpdate.next = (firstInterleavedUpdate: any);
52 - lastInterleavedUpdate.next = (firstPendingUpdate: any);
53 - }
54 - queue.pending = (lastInterleavedUpdate: any);
55 - }
56 - }
57 - interleavedQueues = null;
58 - }
59 -}
packages/react-reconciler/src/ReactFiberInterleavedUpdates.old.js deleted
-59
@@ -1,59 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import type {UpdateQueue as HookQueue} from './ReactFiberHooks.old';
11 -import type {SharedQueue as ClassQueue} from './ReactUpdateQueue.old';
12 -
13 -// An array of all update queues that received updates during the current
14 -// render. When this render exits, either because it finishes or because it is
15 -// interrupted, the interleaved updates will be transferred onto the main part
16 -// of the queue.
17 -let interleavedQueues: Array<
18 - HookQueue<any, any> | ClassQueue<any>,
19 -> | null = null;
20 -
21 -export function pushInterleavedQueue(
22 - queue: HookQueue<any, any> | ClassQueue<any>,
23 -) {
24 - if (interleavedQueues === null) {
25 - interleavedQueues = [queue];
26 - } else {
27 - interleavedQueues.push(queue);
28 - }
29 -}
30 -
31 -export function hasInterleavedUpdates() {
32 - return interleavedQueues !== null;
33 -}
34 -
35 -export function enqueueInterleavedUpdates() {
36 - // Transfer the interleaved updates onto the main queue. Each queue has a
37 - // `pending` field and an `interleaved` field. When they are not null, they
38 - // point to the last node in a circular linked list. We need to append the
39 - // interleaved list to the end of the pending list by joining them into a
40 - // single, circular list.
41 - if (interleavedQueues !== null) {
42 - for (let i = 0; i < interleavedQueues.length; i++) {
43 - const queue = interleavedQueues[i];
44 - const lastInterleavedUpdate = queue.interleaved;
45 - if (lastInterleavedUpdate !== null) {
46 - queue.interleaved = null;
47 - const firstInterleavedUpdate = lastInterleavedUpdate.next;
48 - const lastPendingUpdate = queue.pending;
49 - if (lastPendingUpdate !== null) {
50 - const firstPendingUpdate = lastPendingUpdate.next;
51 - lastPendingUpdate.next = (firstInterleavedUpdate: any);
52 - lastInterleavedUpdate.next = (firstPendingUpdate: any);
53 - }
54 - queue.pending = (lastInterleavedUpdate: any);
55 - }
56 - }
57 - interleavedQueues = null;
58 - }
59 -}
packages/react-reconciler/src/ReactFiberNewContext.new.js
+2 -2
@@ -15,7 +15,7 @@ import type {
15 } from './ReactInternalTypes';
16 import type {StackCursor} from './ReactFiberStack.new';
17 import type {Lanes} from './ReactFiberLane.new';
18 -import type {SharedQueue} from './ReactUpdateQueue.new';
18 +import type {SharedQueue} from './ReactFiberClassUpdateQueue.new';
19
20 import {isPrimaryRenderer} from './ReactFiberHostConfig';
21 import {createCursor, push, pop} from './ReactFiberStack.new';
@@ -39,7 +39,7 @@ import {
39 } from './ReactFiberFlags';
40
41 import is from 'shared/objectIs';
42 -import {createUpdate, ForceUpdate} from './ReactUpdateQueue.new';
42 +import {createUpdate, ForceUpdate} from './ReactFiberClassUpdateQueue.new';
43 import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork.new';
44 import {
45 enableLazyContextPropagation,
packages/react-reconciler/src/ReactFiberNewContext.old.js
+2 -2
@@ -15,7 +15,7 @@ import type {
15 } from './ReactInternalTypes';
16 import type {StackCursor} from './ReactFiberStack.old';
17 import type {Lanes} from './ReactFiberLane.old';
18 -import type {SharedQueue} from './ReactUpdateQueue.old';
18 +import type {SharedQueue} from './ReactFiberClassUpdateQueue.old';
19
20 import {isPrimaryRenderer} from './ReactFiberHostConfig';
21 import {createCursor, push, pop} from './ReactFiberStack.old';
@@ -39,7 +39,7 @@ import {
39 } from './ReactFiberFlags';
40
41 import is from 'shared/objectIs';
42 -import {createUpdate, ForceUpdate} from './ReactUpdateQueue.old';
42 +import {createUpdate, ForceUpdate} from './ReactFiberClassUpdateQueue.old';
43 import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork.old';
44 import {
45 enableLazyContextPropagation,
packages/react-reconciler/src/ReactFiberReconciler.new.js
+58 -20
@@ -68,11 +68,12 @@ import {
68 discreteUpdates,
69 flushPassiveEffects,
70 } from './ReactFiberWorkLoop.new';
71 +import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates.new';
72 import {
73 createUpdate,
74 enqueueUpdate,
75 entangleTransitions,
75 -} from './ReactUpdateQueue.new';
76 +} from './ReactFiberClassUpdateQueue.new';
77 import {
78 isRendering as ReactCurrentFiberIsRendering,
79 current as ReactCurrentFiberCurrent,
@@ -377,9 +378,9 @@ export function updateContainer(
378 update.callback = callback;
379 }
380
380 - enqueueUpdate(current, update, lane);
381 - const root = scheduleUpdateOnFiber(current, lane, eventTime);
381 + const root = enqueueUpdate(current, update, lane);
382 if (root !== null) {
383 + scheduleUpdateOnFiber(root, current, lane, eventTime);
384 entangleTransitions(root, current, lane);
385 }
386
@@ -413,7 +414,7 @@ export function getPublicRootInstance(
414
415 export function attemptSynchronousHydration(fiber: Fiber): void {
416 switch (fiber.tag) {
416 - case HostRoot:
417 + case HostRoot: {
418 const root: FiberRoot = fiber.stateNode;
419 if (isRootDehydrated(root)) {
420 // Flush the first scheduled "update".
@@ -421,15 +422,22 @@ export function attemptSynchronousHydration(fiber: Fiber): void {
422 flushRoot(root, lanes);
423 }
424 break;
424 - case SuspenseComponent:
425 - const eventTime = requestEventTime();
426 - flushSync(() => scheduleUpdateOnFiber(fiber, SyncLane, eventTime));
425 + }
426 + case SuspenseComponent: {
427 + flushSync(() => {
428 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
429 + if (root !== null) {
430 + const eventTime = requestEventTime();
431 + scheduleUpdateOnFiber(root, fiber, SyncLane, eventTime);
432 + }
433 + });
434 // If we're still blocked after this, we need to increase
435 // the priority of any promises resolving within this
436 // boundary so that they next attempt also has higher pri.
437 const retryLane = SyncLane;
438 markRetryLaneIfNotHydrated(fiber, retryLane);
439 break;
440 + }
441 }
442 }
443
@@ -460,9 +468,12 @@ export function attemptDiscreteHydration(fiber: Fiber): void {
468 // Suspense.
469 return;
470 }
463 - const eventTime = requestEventTime();
471 const lane = SyncLane;
465 - scheduleUpdateOnFiber(fiber, lane, eventTime);
472 + const root = enqueueConcurrentRenderForLane(fiber, lane);
473 + if (root !== null) {
474 + const eventTime = requestEventTime();
475 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
476 + }
477 markRetryLaneIfNotHydrated(fiber, lane);
478 }
479
@@ -474,9 +485,12 @@ export function attemptContinuousHydration(fiber: Fiber): void {
485 // Suspense.
486 return;
487 }
477 - const eventTime = requestEventTime();
488 const lane = SelectiveHydrationLane;
479 - scheduleUpdateOnFiber(fiber, lane, eventTime);
489 + const root = enqueueConcurrentRenderForLane(fiber, lane);
490 + if (root !== null) {
491 + const eventTime = requestEventTime();
492 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
493 + }
494 markRetryLaneIfNotHydrated(fiber, lane);
495 }
496
@@ -486,9 +500,12 @@ export function attemptHydrationAtCurrentPriority(fiber: Fiber): void {
500 // their priority other than synchronously flush it.
501 return;
502 }
489 - const eventTime = requestEventTime();
503 const lane = requestUpdateLane(fiber);
491 - scheduleUpdateOnFiber(fiber, lane, eventTime);
504 + const root = enqueueConcurrentRenderForLane(fiber, lane);
505 + if (root !== null) {
506 + const eventTime = requestEventTime();
507 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
508 + }
509 markRetryLaneIfNotHydrated(fiber, lane);
510 }
511
@@ -664,7 +681,10 @@ if (__DEV__) {
681 // Shallow cloning props works as a workaround for now to bypass the bailout check.
682 fiber.memoizedProps = {...fiber.memoizedProps};
683
667 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
684 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
685 + if (root !== null) {
686 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
687 + }
688 }
689 };
690 overrideHookStateDeletePath = (
@@ -685,7 +705,10 @@ if (__DEV__) {
705 // Shallow cloning props works as a workaround for now to bypass the bailout check.
706 fiber.memoizedProps = {...fiber.memoizedProps};
707
688 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
708 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
709 + if (root !== null) {
710 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
711 + }
712 }
713 };
714 overrideHookStateRenamePath = (
@@ -707,7 +730,10 @@ if (__DEV__) {
730 // Shallow cloning props works as a workaround for now to bypass the bailout check.
731 fiber.memoizedProps = {...fiber.memoizedProps};
732
710 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
733 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
734 + if (root !== null) {
735 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
736 + }
737 }
738 };
739
@@ -717,14 +743,20 @@ if (__DEV__) {
743 if (fiber.alternate) {
744 fiber.alternate.pendingProps = fiber.pendingProps;
745 }
720 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
746 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
747 + if (root !== null) {
748 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
749 + }
750 };
751 overridePropsDeletePath = (fiber: Fiber, path: Array<string | number>) => {
752 fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path);
753 if (fiber.alternate) {
754 fiber.alternate.pendingProps = fiber.pendingProps;
755 }
727 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
756 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
757 + if (root !== null) {
758 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
759 + }
760 };
761 overridePropsRenamePath = (
762 fiber: Fiber,
@@ -735,11 +767,17 @@ if (__DEV__) {
767 if (fiber.alternate) {
768 fiber.alternate.pendingProps = fiber.pendingProps;
769 }
738 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
770 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
771 + if (root !== null) {
772 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
773 + }
774 };
775
776 scheduleUpdate = (fiber: Fiber) => {
742 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
777 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
778 + if (root !== null) {
779 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
780 + }
781 };
782
783 setErrorHandler = (newShouldErrorImpl: Fiber => ?boolean) => {
packages/react-reconciler/src/ReactFiberReconciler.old.js
+58 -20
@@ -68,11 +68,12 @@ import {
68 discreteUpdates,
69 flushPassiveEffects,
70 } from './ReactFiberWorkLoop.old';
71 +import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates.old';
72 import {
73 createUpdate,
74 enqueueUpdate,
75 entangleTransitions,
75 -} from './ReactUpdateQueue.old';
76 +} from './ReactFiberClassUpdateQueue.old';
77 import {
78 isRendering as ReactCurrentFiberIsRendering,
79 current as ReactCurrentFiberCurrent,
@@ -377,9 +378,9 @@ export function updateContainer(
378 update.callback = callback;
379 }
380
380 - enqueueUpdate(current, update, lane);
381 - const root = scheduleUpdateOnFiber(current, lane, eventTime);
381 + const root = enqueueUpdate(current, update, lane);
382 if (root !== null) {
383 + scheduleUpdateOnFiber(root, current, lane, eventTime);
384 entangleTransitions(root, current, lane);
385 }
386
@@ -413,7 +414,7 @@ export function getPublicRootInstance(
414
415 export function attemptSynchronousHydration(fiber: Fiber): void {
416 switch (fiber.tag) {
416 - case HostRoot:
417 + case HostRoot: {
418 const root: FiberRoot = fiber.stateNode;
419 if (isRootDehydrated(root)) {
420 // Flush the first scheduled "update".
@@ -421,15 +422,22 @@ export function attemptSynchronousHydration(fiber: Fiber): void {
422 flushRoot(root, lanes);
423 }
424 break;
424 - case SuspenseComponent:
425 - const eventTime = requestEventTime();
426 - flushSync(() => scheduleUpdateOnFiber(fiber, SyncLane, eventTime));
425 + }
426 + case SuspenseComponent: {
427 + flushSync(() => {
428 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
429 + if (root !== null) {
430 + const eventTime = requestEventTime();
431 + scheduleUpdateOnFiber(root, fiber, SyncLane, eventTime);
432 + }
433 + });
434 // If we're still blocked after this, we need to increase
435 // the priority of any promises resolving within this
436 // boundary so that they next attempt also has higher pri.
437 const retryLane = SyncLane;
438 markRetryLaneIfNotHydrated(fiber, retryLane);
439 break;
440 + }
441 }
442 }
443
@@ -460,9 +468,12 @@ export function attemptDiscreteHydration(fiber: Fiber): void {
468 // Suspense.
469 return;
470 }
463 - const eventTime = requestEventTime();
471 const lane = SyncLane;
465 - scheduleUpdateOnFiber(fiber, lane, eventTime);
472 + const root = enqueueConcurrentRenderForLane(fiber, lane);
473 + if (root !== null) {
474 + const eventTime = requestEventTime();
475 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
476 + }
477 markRetryLaneIfNotHydrated(fiber, lane);
478 }
479
@@ -474,9 +485,12 @@ export function attemptContinuousHydration(fiber: Fiber): void {
485 // Suspense.
486 return;
487 }
477 - const eventTime = requestEventTime();
488 const lane = SelectiveHydrationLane;
479 - scheduleUpdateOnFiber(fiber, lane, eventTime);
489 + const root = enqueueConcurrentRenderForLane(fiber, lane);
490 + if (root !== null) {
491 + const eventTime = requestEventTime();
492 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
493 + }
494 markRetryLaneIfNotHydrated(fiber, lane);
495 }
496
@@ -486,9 +500,12 @@ export function attemptHydrationAtCurrentPriority(fiber: Fiber): void {
500 // their priority other than synchronously flush it.
501 return;
502 }
489 - const eventTime = requestEventTime();
503 const lane = requestUpdateLane(fiber);
491 - scheduleUpdateOnFiber(fiber, lane, eventTime);
504 + const root = enqueueConcurrentRenderForLane(fiber, lane);
505 + if (root !== null) {
506 + const eventTime = requestEventTime();
507 + scheduleUpdateOnFiber(root, fiber, lane, eventTime);
508 + }
509 markRetryLaneIfNotHydrated(fiber, lane);
510 }
511
@@ -664,7 +681,10 @@ if (__DEV__) {
681 // Shallow cloning props works as a workaround for now to bypass the bailout check.
682 fiber.memoizedProps = {...fiber.memoizedProps};
683
667 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
684 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
685 + if (root !== null) {
686 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
687 + }
688 }
689 };
690 overrideHookStateDeletePath = (
@@ -685,7 +705,10 @@ if (__DEV__) {
705 // Shallow cloning props works as a workaround for now to bypass the bailout check.
706 fiber.memoizedProps = {...fiber.memoizedProps};
707
688 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
708 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
709 + if (root !== null) {
710 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
711 + }
712 }
713 };
714 overrideHookStateRenamePath = (
@@ -707,7 +730,10 @@ if (__DEV__) {
730 // Shallow cloning props works as a workaround for now to bypass the bailout check.
731 fiber.memoizedProps = {...fiber.memoizedProps};
732
710 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
733 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
734 + if (root !== null) {
735 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
736 + }
737 }
738 };
739
@@ -717,14 +743,20 @@ if (__DEV__) {
743 if (fiber.alternate) {
744 fiber.alternate.pendingProps = fiber.pendingProps;
745 }
720 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
746 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
747 + if (root !== null) {
748 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
749 + }
750 };
751 overridePropsDeletePath = (fiber: Fiber, path: Array<string | number>) => {
752 fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path);
753 if (fiber.alternate) {
754 fiber.alternate.pendingProps = fiber.pendingProps;
755 }
727 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
756 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
757 + if (root !== null) {
758 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
759 + }
760 };
761 overridePropsRenamePath = (
762 fiber: Fiber,
@@ -735,11 +767,17 @@ if (__DEV__) {
767 if (fiber.alternate) {
768 fiber.alternate.pendingProps = fiber.pendingProps;
769 }
738 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
770 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
771 + if (root !== null) {
772 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
773 + }
774 };
775
776 scheduleUpdate = (fiber: Fiber) => {
742 - scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
777 + const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
778 + if (root !== null) {
779 + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
780 + }
781 };
782
783 setErrorHandler = (newShouldErrorImpl: Fiber => ?boolean) => {
packages/react-reconciler/src/ReactFiberRoot.new.js
+1 -1
@@ -37,7 +37,7 @@ import {
37 enableUpdaterTracking,
38 enableTransitionTracing,
39 } from 'shared/ReactFeatureFlags';
40 -import {initializeUpdateQueue} from './ReactUpdateQueue.new';
40 +import {initializeUpdateQueue} from './ReactFiberClassUpdateQueue.new';
41 import {LegacyRoot, ConcurrentRoot} from './ReactRootTags';
42 import {createCache, retainCache} from './ReactFiberCacheComponent.new';
43
packages/react-reconciler/src/ReactFiberRoot.old.js
+1 -1
@@ -37,7 +37,7 @@ import {
37 enableUpdaterTracking,
38 enableTransitionTracing,
39 } from 'shared/ReactFeatureFlags';
40 -import {initializeUpdateQueue} from './ReactUpdateQueue.old';
40 +import {initializeUpdateQueue} from './ReactFiberClassUpdateQueue.old';
41 import {LegacyRoot, ConcurrentRoot} from './ReactRootTags';
42 import {createCache, retainCache} from './ReactFiberCacheComponent.old';
43
packages/react-reconciler/src/ReactFiberThrow.new.js
+2 -2
@@ -11,7 +11,7 @@ import type {Fiber} from './ReactInternalTypes';
11 import type {FiberRoot} from './ReactInternalTypes';
12 import type {Lane, Lanes} from './ReactFiberLane.new';
13 import type {CapturedValue} from './ReactCapturedValue';
14 -import type {Update} from './ReactUpdateQueue.new';
14 +import type {Update} from './ReactFiberClassUpdateQueue.new';
15 import type {Wakeable} from 'shared/ReactTypes';
16 import type {SuspenseContext} from './ReactFiberSuspenseContext.new';
17
@@ -48,7 +48,7 @@ import {
48 CaptureUpdate,
49 ForceUpdate,
50 enqueueUpdate,
51 -} from './ReactUpdateQueue.new';
51 +} from './ReactFiberClassUpdateQueue.new';
52 import {markFailedErrorBoundaryForHotReloading} from './ReactFiberHotReloading.new';
53 import {
54 suspenseStackCursor,
packages/react-reconciler/src/ReactFiberThrow.old.js
+2 -2
@@ -11,7 +11,7 @@ import type {Fiber} from './ReactInternalTypes';
11 import type {FiberRoot} from './ReactInternalTypes';
12 import type {Lane, Lanes} from './ReactFiberLane.old';
13 import type {CapturedValue} from './ReactCapturedValue';
14 -import type {Update} from './ReactUpdateQueue.old';
14 +import type {Update} from './ReactFiberClassUpdateQueue.old';
15 import type {Wakeable} from 'shared/ReactTypes';
16 import type {SuspenseContext} from './ReactFiberSuspenseContext.old';
17
@@ -48,7 +48,7 @@ import {
48 CaptureUpdate,
49 ForceUpdate,
50 enqueueUpdate,
51 -} from './ReactUpdateQueue.old';
51 +} from './ReactFiberClassUpdateQueue.old';
52 import {markFailedErrorBoundaryForHotReloading} from './ReactFiberHotReloading.old';
53 import {
54 suspenseStackCursor,
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+27 -90
@@ -105,11 +105,9 @@ import {
105 import {LegacyRoot} from './ReactRootTags';
106 import {
107 NoFlags,
108 - Placement,
108 Incomplete,
109 StoreConsistency,
110 HostEffectMask,
112 - Hydrating,
111 ForceClientRender,
112 BeforeMutationMask,
113 MutationMask,
@@ -182,7 +180,7 @@ import {
180 invokePassiveEffectUnmountInDEV,
181 reportUncaughtErrorInDEV,
182 } from './ReactFiberCommitWork.new';
185 -import {enqueueUpdate} from './ReactUpdateQueue.new';
183 +import {enqueueUpdate} from './ReactFiberClassUpdateQueue.new';
184 import {resetContextDependencies} from './ReactFiberNewContext.new';
185 import {
186 resetHooksAfterThrow,
@@ -196,9 +194,9 @@ import {
194 createCursor,
195 } from './ReactFiberStack.new';
196 import {
199 - enqueueInterleavedUpdates,
200 - hasInterleavedUpdates,
201 -} from './ReactFiberInterleavedUpdates.new';
197 + enqueueConcurrentRenderForLane,
198 + finishQueueingConcurrentUpdates,
199 +} from './ReactFiberConcurrentUpdates.new';
200
201 import {
202 markNestedUpdateScheduled,
@@ -521,10 +519,11 @@ function requestRetryLane(fiber: Fiber) {
519 }
520
521 export function scheduleUpdateOnFiber(
522 + root: FiberRoot,
523 fiber: Fiber,
524 lane: Lane,
525 eventTime: number,
527 -): FiberRoot | null {
526 +) {
527 checkForNestedUpdates();
528
529 if (__DEV__) {
@@ -533,11 +532,6 @@ export function scheduleUpdateOnFiber(
532 }
533 }
534
536 - const root = markUpdateLaneFromFiberToRoot(fiber, lane);
537 - if (root === null) {
538 - return null;
539 - }
540 -
535 if (__DEV__) {
536 if (isFlushingPassiveEffects) {
537 didScheduleUpdateDuringPassiveEffects = true;
@@ -606,8 +600,6 @@ export function scheduleUpdateOnFiber(
600 }
601
602 if (root === workInProgressRoot) {
609 - // TODO: Consolidate with `isInterleavedUpdate` check
610 -
603 // Received an update to a tree that's in the middle of rendering. Mark
604 // that there was an interleaved update work on this root. Unless the
605 // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render
@@ -650,7 +642,6 @@ export function scheduleUpdateOnFiber(
642 flushSyncCallbacksOnlyInLegacyMode();
643 }
644 }
653 - return root;
645 }
646
647 export function scheduleInitialHydrationOnRoot(
@@ -673,73 +664,15 @@ export function scheduleInitialHydrationOnRoot(
664 ensureRootIsScheduled(root, eventTime);
665 }
666
676 -// This is split into a separate function so we can mark a fiber with pending
677 -// work without treating it as a typical update that originates from an event;
678 -// e.g. retrying a Suspense boundary isn't an update, but it does schedule work
679 -// on a fiber.
680 -function markUpdateLaneFromFiberToRoot(
681 - sourceFiber: Fiber,
682 - lane: Lane,
683 -): FiberRoot | null {
684 - // Update the source fiber's lanes
685 - sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
686 - let alternate = sourceFiber.alternate;
687 - if (alternate !== null) {
688 - alternate.lanes = mergeLanes(alternate.lanes, lane);
689 - }
690 - if (__DEV__) {
691 - if (
692 - alternate === null &&
693 - (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags
694 - ) {
695 - warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
696 - }
697 - }
698 - // Walk the parent path to the root and update the child lanes.
699 - let node = sourceFiber;
700 - let parent = sourceFiber.return;
701 - while (parent !== null) {
702 - parent.childLanes = mergeLanes(parent.childLanes, lane);
703 - alternate = parent.alternate;
704 - if (alternate !== null) {
705 - alternate.childLanes = mergeLanes(alternate.childLanes, lane);
706 - } else {
707 - if (__DEV__) {
708 - if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {
709 - warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
710 - }
711 - }
712 - }
713 - node = parent;
714 - parent = parent.return;
715 - }
716 - if (node.tag === HostRoot) {
717 - const root: FiberRoot = node.stateNode;
718 - return root;
719 - } else {
720 - return null;
721 - }
722 -}
723 -
724 -export function isInterleavedUpdate(fiber: Fiber, lane: Lane) {
667 +export function isUnsafeClassRenderPhaseUpdate(fiber: Fiber) {
668 + // Check if this is a render phase update. Only called by class components,
669 + // which special (deprecated) behavior for UNSAFE_componentWillReceive props.
670 return (
726 - // TODO: Optimize slightly by comparing to root that fiber belongs to.
727 - // Requires some refactoring. Not a big deal though since it's rare for
728 - // concurrent apps to have more than a single root.
729 - (workInProgressRoot !== null ||
730 - // If the interleaved updates queue hasn't been cleared yet, then
731 - // we should treat this as an interleaved update, too. This is also a
732 - // defensive coding measure in case a new update comes in between when
733 - // rendering has finished and when the interleaved updates are transferred
734 - // to the main queue.
735 - hasInterleavedUpdates()) &&
736 - (fiber.mode & ConcurrentMode) !== NoMode &&
737 - // If this is a render phase update (i.e. UNSAFE_componentWillReceiveProps),
738 - // then don't treat this as an interleaved update. This pattern is
739 - // accompanied by a warning but we haven't fully deprecated it yet. We can
740 - // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled.
741 - (deferRenderPhaseUpdateToNextBatch ||
742 - (executionContext & RenderContext) === NoContext)
671 + // TODO: Remove outdated deferRenderPhaseUpdateToNextBatch experiment. We
672 + // decided not to enable it.
673 + (!deferRenderPhaseUpdateToNextBatch ||
674 + (fiber.mode & ConcurrentMode) === NoMode) &&
675 + (executionContext & RenderContext) !== NoContext
676 );
677 }
678
@@ -1541,7 +1474,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
1474 workInProgressRootConcurrentErrors = null;
1475 workInProgressRootRecoverableErrors = null;
1476
1544 - enqueueInterleavedUpdates();
1477 + finishQueueingConcurrentUpdates();
1478
1479 if (__DEV__) {
1480 ReactStrictModeWarnings.discardPendingWarnings();
@@ -2113,9 +2046,15 @@ function commitRootImpl(
2046 root.callbackNode = null;
2047 root.callbackPriority = NoLane;
2048
2116 - // Update the first and last pending times on this root. The new first
2117 - // pending time is whatever is left on the root fiber.
2049 + // Check which lanes no longer have any work scheduled on them, and mark
2050 + // those as finished.
2051 let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
2052 +
2053 + // Make sure to account for lanes that were updated by a concurrent event
2054 + // during the render phase; don't mark them as finished.
2055 + const concurrentlyUpdatedLanes = finishQueueingConcurrentUpdates();
2056 + remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes);
2057 +
2058 markRootFinished(root, remainingLanes);
2059
2060 if (root === workInProgressRoot) {
@@ -2617,9 +2556,8 @@ function captureCommitPhaseErrorOnRoot(
2556 ) {
2557 const errorInfo = createCapturedValue(error, sourceFiber);
2558 const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));
2620 - enqueueUpdate(rootFiber, update, (SyncLane: Lane));
2559 + const root = enqueueUpdate(rootFiber, update, (SyncLane: Lane));
2560 const eventTime = requestEventTime();
2622 - const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));
2561 if (root !== null) {
2562 markRootUpdated(root, SyncLane, eventTime);
2563 ensureRootIsScheduled(root, eventTime);
@@ -2667,9 +2605,8 @@ export function captureCommitPhaseError(
2605 errorInfo,
2606 (SyncLane: Lane),
2607 );
2670 - enqueueUpdate(fiber, update, (SyncLane: Lane));
2608 + const root = enqueueUpdate(fiber, update, (SyncLane: Lane));
2609 const eventTime = requestEventTime();
2672 - const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));
2610 if (root !== null) {
2611 markRootUpdated(root, SyncLane, eventTime);
2612 ensureRootIsScheduled(root, eventTime);
@@ -2760,7 +2697,7 @@ function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
2697 }
2698 // TODO: Special case idle priority?
2699 const eventTime = requestEventTime();
2763 - const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);
2700 + const root = enqueueConcurrentRenderForLane(boundaryFiber, retryLane);
2701 if (root !== null) {
2702 markRootUpdated(root, retryLane, eventTime);
2703 ensureRootIsScheduled(root, eventTime);
@@ -2931,7 +2868,7 @@ function invokeEffectsInDev(
2868 }
2869
2870 let didWarnStateUpdateForNotYetMountedComponent: Set<string> | null = null;
2934 -function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {
2871 +export function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber: Fiber) {
2872 if (__DEV__) {
2873 if ((executionContext & RenderContext) !== NoContext) {
2874 // We let the other warning about render phase updates deal with this one.
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+19 -88
@@ -105,11 +105,9 @@ import {
105 import {LegacyRoot} from './ReactRootTags';
106 import {
107 NoFlags,
108 - Placement,
108 Incomplete,
109 StoreConsistency,
110 HostEffectMask,
112 - Hydrating,
111 ForceClientRender,
112 BeforeMutationMask,
113 MutationMask,
@@ -182,7 +180,7 @@ import {
180 invokePassiveEffectUnmountInDEV,
181 reportUncaughtErrorInDEV,
182 } from './ReactFiberCommitWork.old';
185 -import {enqueueUpdate} from './ReactUpdateQueue.old';
183 +import {enqueueUpdate} from './ReactFiberClassUpdateQueue.old';
184 import {resetContextDependencies} from './ReactFiberNewContext.old';
185 import {
186 resetHooksAfterThrow,
@@ -196,9 +194,9 @@ import {
194 createCursor,
195 } from './ReactFiberStack.old';
196 import {
199 - enqueueInterleavedUpdates,
200 - hasInterleavedUpdates,
201 -} from './ReactFiberInterleavedUpdates.old';
197 + enqueueConcurrentRenderForLane,
198 + finishQueueingConcurrentUpdates,
199 +} from './ReactFiberConcurrentUpdates.old';
200
201 import {
202 markNestedUpdateScheduled,
@@ -521,10 +519,11 @@ function requestRetryLane(fiber: Fiber) {
519 }
520
521 export function scheduleUpdateOnFiber(
522 + root: FiberRoot,
523 fiber: Fiber,
524 lane: Lane,
525 eventTime: number,
527 -): FiberRoot | null {
526 +) {
527 checkForNestedUpdates();
528
529 if (__DEV__) {
@@ -533,11 +532,6 @@ export function scheduleUpdateOnFiber(
532 }
533 }
534
536 - const root = markUpdateLaneFromFiberToRoot(fiber, lane);
537 - if (root === null) {
538 - return null;
539 - }
540 -
535 if (__DEV__) {
536 if (isFlushingPassiveEffects) {
537 didScheduleUpdateDuringPassiveEffects = true;
@@ -606,8 +600,6 @@ export function scheduleUpdateOnFiber(
600 }
601
602 if (root === workInProgressRoot) {
609 - // TODO: Consolidate with `isInterleavedUpdate` check
610 -
603 // Received an update to a tree that's in the middle of rendering. Mark
604 // that there was an interleaved update work on this root. Unless the
605 // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render
@@ -650,7 +642,6 @@ export function scheduleUpdateOnFiber(
642 flushSyncCallbacksOnlyInLegacyMode();
643 }
644 }
653 - return root;
645 }
646
647 export function scheduleInitialHydrationOnRoot(
@@ -673,73 +664,15 @@ export function scheduleInitialHydrationOnRoot(
664 ensureRootIsScheduled(root, eventTime);
665 }
666
676 -// This is split into a separate function so we can mark a fiber with pending
677 -// work without treating it as a typical update that originates from an event;
678 -// e.g. retrying a Suspense boundary isn't an update, but it does schedule work
679 -// on a fiber.
680 -function markUpdateLaneFromFiberToRoot(
681 - sourceFiber: Fiber,
682 - lane: Lane,
683 -): FiberRoot | null {
684 - // Update the source fiber's lanes
685 - sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
686 - let alternate = sourceFiber.alternate;
687 - if (alternate !== null) {
688 - alternate.lanes = mergeLanes(alternate.lanes, lane);
689 - }
690 - if (__DEV__) {
691 - if (
692 - alternate === null &&
693 - (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags
694 - ) {
695 - warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
696 - }
697 - }
698 - // Walk the parent path to the root and update the child lanes.
699 - let node = sourceFiber;
700 - let parent = sourceFiber.return;
701 - while (parent !== null) {
702 - parent.childLanes = mergeLanes(parent.childLanes, lane);
703 - alternate = parent.alternate;
704 - if (alternate !== null) {
705 - alternate.childLanes = mergeLanes(alternate.childLanes, lane);
706 - } else {
707 - if (__DEV__) {
708 - if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {
709 - warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
710 - }
711 - }
712 - }
713 - node = parent;
714 - parent = parent.return;
715 - }
716 - if (node.tag === HostRoot) {
717 - const root: FiberRoot = node.stateNode;
718 - return root;
719 - } else {
720 - return null;
721 - }
722 -}
723 -
724 -export function isInterleavedUpdate(fiber: Fiber, lane: Lane) {
667 +export function isUnsafeClassRenderPhaseUpdate(fiber: Fiber) {
668 + // Check if this is a render phase update. Only called by class components,
669 + // which special (deprecated) behavior for UNSAFE_componentWillReceive props.
670 return (
726 - // TODO: Optimize slightly by comparing to root that fiber belongs to.
727 - // Requires some refactoring. Not a big deal though since it's rare for
728 - // concurrent apps to have more than a single root.
729 - (workInProgressRoot !== null ||
730 - // If the interleaved updates queue hasn't been cleared yet, then
731 - // we should treat this as an interleaved update, too. This is also a
732 - // defensive coding measure in case a new update comes in between when
733 - // rendering has finished and when the interleaved updates are transferred
734 - // to the main queue.
735 - hasInterleavedUpdates()) &&
736 - (fiber.mode & ConcurrentMode) !== NoMode &&
737 - // If this is a render phase update (i.e. UNSAFE_componentWillReceiveProps),
738 - // then don't treat this as an interleaved update. This pattern is
739 - // accompanied by a warning but we haven't fully deprecated it yet. We can
740 - // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled.
741 - (deferRenderPhaseUpdateToNextBatch ||
742 - (executionContext & RenderContext) === NoContext)
671 + // TODO: Remove outdated deferRenderPhaseUpdateToNextBatch experiment. We
672 + // decided not to enable it.
673 + (!deferRenderPhaseUpdateToNextBatch ||
674 + (fiber.mode & ConcurrentMode) === NoMode) &&
675 + (executionContext & RenderContext) !== NoContext
676 );
677 }
678
@@ -1541,7 +1474,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
1474 workInProgressRootConcurrentErrors = null;
1475 workInProgressRootRecoverableErrors = null;
1476
1544 - enqueueInterleavedUpdates();
1477 + finishQueueingConcurrentUpdates();
1478
1479 if (__DEV__) {
1480 ReactStrictModeWarnings.discardPendingWarnings();
@@ -2617,9 +2550,8 @@ function captureCommitPhaseErrorOnRoot(
2550 ) {
2551 const errorInfo = createCapturedValue(error, sourceFiber);
2552 const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));
2620 - enqueueUpdate(rootFiber, update, (SyncLane: Lane));
2553 + const root = enqueueUpdate(rootFiber, update, (SyncLane: Lane));
2554 const eventTime = requestEventTime();
2622 - const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));
2555 if (root !== null) {
2556 markRootUpdated(root, SyncLane, eventTime);
2557 ensureRootIsScheduled(root, eventTime);
@@ -2667,9 +2599,8 @@ export function captureCommitPhaseError(
2599 errorInfo,
2600 (SyncLane: Lane),
2601 );
2670 - enqueueUpdate(fiber, update, (SyncLane: Lane));
2602 + const root = enqueueUpdate(fiber, update, (SyncLane: Lane));
2603 const eventTime = requestEventTime();
2672 - const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));
2604 if (root !== null) {
2605 markRootUpdated(root, SyncLane, eventTime);
2606 ensureRootIsScheduled(root, eventTime);
@@ -2760,7 +2691,7 @@ function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
2691 }
2692 // TODO: Special case idle priority?
2693 const eventTime = requestEventTime();
2763 - const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);
2694 + const root = enqueueConcurrentRenderForLane(boundaryFiber, retryLane);
2695 if (root !== null) {
2696 markRootUpdated(root, retryLane, eventTime);
2697 ensureRootIsScheduled(root, eventTime);
@@ -2931,7 +2862,7 @@ function invokeEffectsInDev(
2862 }
2863
2864 let didWarnStateUpdateForNotYetMountedComponent: Set<string> | null = null;
2934 -function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {
2865 +export function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber: Fiber) {
2866 if (__DEV__) {
2867 if ((executionContext & RenderContext) !== NoContext) {
2868 // We let the other warning about render phase updates deal with this one.
scripts/merge-fork/forked-revisions
+1
@@ -0,0 +1 @@
1 +17691acc071d56261d43c3cf183f287d983baa9b [FORKED] Don't update childLanes until after current render