Skip to content

Commit 9ae2db3

Browse files
committed
[WIP] Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have exposed some flaws in how it's modeled. Upcoming features like Suspense and [redacted] also rely on the update queue in ways that weren't anticipated in the original design. Major changes: - Instead of boolean flags for `isReplace` and `isForceUpdate`, updates have a `tag` field (like Fiber). This lowers the cost for adding new types of updates. - Render phase updates are special cased. Updates scheduled during the render phase are dropped if the work-in-progress does not commit. This is used for `getDerivedStateFrom{Props,Catch}`. - `callbackList` has been replaced with a generic effect list. Aside from callbacks, this is also used for `componentDidCatch`. TODO: - [x] Class components - [ ] Host roots - [ ] Fix Flow
1 parent 76b4ba0 commit 9ae2db3

11 files changed

+1068
-458
lines changed

packages/react-reconciler/src/ReactFiberBeginWork.js

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ import {
3535
ContextConsumer,
3636
} from 'shared/ReactTypeOfWork';
3737
import {
38+
NoEffect,
3839
PerformedWork,
3940
Placement,
4041
ContentReset,
4142
Ref,
43+
DidCapture,
4244
} from 'shared/ReactTypeOfSideEffect';
4345
import {ReactCurrentOwner} from 'shared/ReactGlobalSharedState';
4446
import {
@@ -58,7 +60,12 @@ import {
5860
reconcileChildFibers,
5961
cloneChildFibers,
6062
} from './ReactChildFiber';
61-
import {processUpdateQueue} from './ReactFiberUpdateQueue';
63+
import {processUpdateQueue as processLegacyUpdateQueue} from './ReactFiberUpdateQueue';
64+
import {
65+
createDeriveStateFromPropsUpdate,
66+
enqueueRenderPhaseUpdate,
67+
processUpdateQueue,
68+
} from './ReactUpdateQueue';
6269
import {NoWork, Never} from './ReactFiberExpirationTime';
6370
import {AsyncMode, StrictMode} from './ReactTypeOfMode';
6471
import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt';
@@ -260,7 +267,11 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
260267
if (current === null) {
261268
if (workInProgress.stateNode === null) {
262269
// In the initial pass we might need to construct the instance.
263-
constructClassInstance(workInProgress, workInProgress.pendingProps);
270+
constructClassInstance(
271+
workInProgress,
272+
workInProgress.pendingProps,
273+
renderExpirationTime,
274+
);
264275
mountClassInstance(workInProgress, renderExpirationTime);
265276

266277
shouldUpdate = true;
@@ -278,22 +289,11 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
278289
renderExpirationTime,
279290
);
280291
}
281-
282-
// We processed the update queue inside updateClassInstance. It may have
283-
// included some errors that were dispatched during the commit phase.
284-
// TODO: Refactor class components so this is less awkward.
285-
let didCaptureError = false;
286-
const updateQueue = workInProgress.updateQueue;
287-
if (updateQueue !== null && updateQueue.capturedValues !== null) {
288-
shouldUpdate = true;
289-
didCaptureError = true;
290-
}
291292
return finishClassComponent(
292293
current,
293294
workInProgress,
294295
shouldUpdate,
295296
hasContext,
296-
didCaptureError,
297297
renderExpirationTime,
298298
);
299299
}
@@ -303,12 +303,14 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
303303
workInProgress: Fiber,
304304
shouldUpdate: boolean,
305305
hasContext: boolean,
306-
didCaptureError: boolean,
307306
renderExpirationTime: ExpirationTime,
308307
) {
309308
// Refs should update even if shouldComponentUpdate returns false
310309
markRef(current, workInProgress);
311310

311+
const didCaptureError =
312+
(workInProgress.effectTag & DidCapture) !== NoEffect;
313+
312314
if (!shouldUpdate && !didCaptureError) {
313315
// Context providers should defer to sCU for rendering
314316
if (hasContext) {
@@ -414,7 +416,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
414416
let updateQueue = workInProgress.updateQueue;
415417
if (updateQueue !== null) {
416418
const prevState = workInProgress.memoizedState;
417-
const state = processUpdateQueue(
419+
const state = processLegacyUpdateQueue(
418420
current,
419421
workInProgress,
420422
updateQueue,
@@ -607,20 +609,13 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
607609
workInProgress.memoizedState =
608610
value.state !== null && value.state !== undefined ? value.state : null;
609611

610-
if (typeof Component.getDerivedStateFromProps === 'function') {
611-
const partialState = callGetDerivedStateFromProps(
612-
workInProgress,
613-
value,
614-
props,
615-
workInProgress.memoizedState,
616-
);
617-
618-
if (partialState !== null && partialState !== undefined) {
619-
workInProgress.memoizedState = Object.assign(
620-
{},
621-
workInProgress.memoizedState,
622-
partialState,
623-
);
612+
const getDerivedStateFromProps = Component.getDerivedStateFromProps;
613+
if (typeof getDerivedStateFromProps === 'function') {
614+
const update = createDeriveStateFromPropsUpdate(renderExpirationTime);
615+
enqueueRenderPhaseUpdate(workInProgress, update, renderExpirationTime);
616+
const updateQueue = workInProgress.updateQueue;
617+
if (updateQueue !== null) {
618+
processUpdateQueue(workInProgress, updateQueue, renderExpirationTime);
624619
}
625620
}
626621

@@ -635,7 +630,6 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
635630
workInProgress,
636631
true,
637632
hasContext,
638-
false,
639633
renderExpirationTime,
640634
);
641635
} else {

0 commit comments

Comments
 (0)