Skip to content

Commit 7bf40e1

Browse files
authored
Initialize update queue object on mount (#17560)
* Refactor Update Queues to Fix Rebasing Bug Fixes a bug related to rebasing updates. Once an update has committed, it should never un-commit, even if interrupted by a higher priority update. The fix includes a refactor of how update queues work. This commit is a combination of two PRs: - #17483 by @sebmarkbage refactors the hook update queue - #17510 by @acdlite refactors the class and root update queue Landing one without the other would cause state updates to sometimes be inconsistent across components, so I've combined them into a single commit in case they need to be reverted. Co-authored-by: Sebastian Markbåge <sema@fb.com> Co-authored-by: Andrew Clark <git@andrewclark.io> * Initialize update queue object on mount Instead of lazily initializing update queue objects on the first update, class and host root queues are created on mount. This simplifies the logic for appending new updates and matches what we do for hooks.
1 parent 031a5aa commit 7bf40e1

8 files changed

Lines changed: 530 additions & 442 deletions

File tree

‎packages/react-noop-renderer/src/createReactNoop.js‎

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,20 +1142,33 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
11421142

11431143
functionlogUpdateQueue(updateQueue: UpdateQueue<mixed>,depth){
11441144
log(' '.repeat(depth+1)+'QUEUED UPDATES');
1145-
constfirstUpdate=updateQueue.firstUpdate;
1146-
if(!firstUpdate){
1145+
constlast=updateQueue.baseQueue;
1146+
if(last===null){
11471147
return;
11481148
}
1149+
constfirst=last.next;
1150+
letupdate=first;
1151+
if(update!==null){
1152+
do{
1153+
log(
1154+
' '.repeat(depth+1)+'~',
1155+
'['+update.expirationTime+']',
1156+
);
1157+
}while(update!==null&&update!==first);
1158+
}
11491159

1150-
log(
1151-
' '.repeat(depth+1)+'~',
1152-
'['+firstUpdate.expirationTime+']',
1153-
);
1154-
while(firstUpdate.next){
1155-
log(
1156-
' '.repeat(depth+1)+'~',
1157-
'['+firstUpdate.expirationTime+']',
1158-
);
1160+
constlastPending=updateQueue.shared.pending;
1161+
if(lastPending!==null){
1162+
constfirstPending=lastPending.next;
1163+
letpendingUpdate=firstPending;
1164+
if(pendingUpdate!==null){
1165+
do{
1166+
log(
1167+
' '.repeat(depth+1)+'~',
1168+
'['+pendingUpdate.expirationTime+']',
1169+
);
1170+
}while(pendingUpdate!==null&&pendingUpdate!==firstPending);
1171+
}
11591172
}
11601173
}
11611174

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,11 @@ import {
9090
reconcileChildFibers,
9191
cloneChildFibers,
9292
}from'./ReactChildFiber';
93-
import{processUpdateQueue}from'./ReactUpdateQueue';
93+
import{
94+
processUpdateQueue,
95+
cloneUpdateQueue,
96+
initializeUpdateQueue,
97+
}from'./ReactUpdateQueue';
9498
import{
9599
NoWork,
96100
Never,
@@ -904,21 +908,16 @@ function updateHostRoot(current, workInProgress, renderExpirationTime) {
904908
pushHostRootContext(workInProgress);
905909
constupdateQueue=workInProgress.updateQueue;
906910
invariant(
907-
updateQueue!==null,
911+
current!==null&&updateQueue!==null,
908912
'If the root does not have an updateQueue, we should have already '+
909913
'bailed out. This error is likely caused by a bug in React. Please '+
910914
'file an issue.',
911915
);
912916
constnextProps=workInProgress.pendingProps;
913917
constprevState=workInProgress.memoizedState;
914918
constprevChildren=prevState!==null ? prevState.element : null;
915-
processUpdateQueue(
916-
workInProgress,
917-
updateQueue,
918-
nextProps,
919-
null,
920-
renderExpirationTime,
921-
);
919+
cloneUpdateQueue(current,workInProgress);
920+
processUpdateQueue(workInProgress,nextProps,null,renderExpirationTime);
922921
constnextState=workInProgress.memoizedState;
923922
// Caution: React DevTools currently depends on this property
924923
// being called "element".
@@ -1338,6 +1337,8 @@ function mountIndeterminateComponent(
13381337
workInProgress.memoizedState=
13391338
value.state!==null&&value.state!==undefined ? value.state : null;
13401339

1340+
initializeUpdateQueue(workInProgress);
1341+
13411342
constgetDerivedStateFromProps=Component.getDerivedStateFromProps;
13421343
if(typeofgetDerivedStateFromProps==='function'){
13431344
applyDerivedStateFromProps(

‎packages/react-reconciler/src/ReactFiberClassComponent.js‎

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
importtype{Fiber}from'./ReactFiber';
1111
importtype{ExpirationTime}from'./ReactFiberExpirationTime';
12+
importtype{UpdateQueue}from'./ReactUpdateQueue';
1213

1314
importReactfrom'react';
1415
import{Update,Snapshot}from'shared/ReactSideEffectTags';
@@ -38,6 +39,8 @@ import {
3839
createUpdate,
3940
ReplaceState,
4041
ForceUpdate,
42+
initializeUpdateQueue,
43+
cloneUpdateQueue,
4144
}from'./ReactUpdateQueue';
4245
import{NoWork}from'./ReactFiberExpirationTime';
4346
import{
@@ -171,8 +174,9 @@ export function applyDerivedStateFromProps(
171174

172175
// Once the update queue is empty, persist the derived state onto the
173176
// base state.
174-
constupdateQueue=workInProgress.updateQueue;
175-
if(updateQueue!==null&&workInProgress.expirationTime===NoWork){
177+
if(workInProgress.expirationTime===NoWork){
178+
// Queue is always non-null for classes
179+
constupdateQueue: UpdateQueue<any> = (workInProgress.updateQueue: any);
176180
updateQueue.baseState = memoizedState;
177181
}
178182
}
@@ -789,6 +793,8 @@ function mountClassInstance(
789793
instance.state=workInProgress.memoizedState;
790794
instance.refs=emptyRefsObject;
791795

796+
initializeUpdateQueue(workInProgress);
797+
792798
constcontextType=ctor.contextType;
793799
if(typeofcontextType==='object'&&contextType!==null){
794800
instance.context=readContext(contextType);
@@ -829,17 +835,8 @@ function mountClassInstance(
829835
}
830836
}
831837

832-
letupdateQueue=workInProgress.updateQueue;
833-
if(updateQueue!==null){
834-
processUpdateQueue(
835-
workInProgress,
836-
updateQueue,
837-
newProps,
838-
instance,
839-
renderExpirationTime,
840-
);
841-
instance.state=workInProgress.memoizedState;
842-
}
838+
processUpdateQueue(workInProgress,newProps,instance,renderExpirationTime);
839+
instance.state=workInProgress.memoizedState;
843840

844841
constgetDerivedStateFromProps=ctor.getDerivedStateFromProps;
845842
if(typeofgetDerivedStateFromProps==='function'){
@@ -863,17 +860,13 @@ function mountClassInstance(
863860
callComponentWillMount(workInProgress,instance);
864861
// If we had additional state updates during this life-cycle, let's
865862
// process them now.
866-
updateQueue=workInProgress.updateQueue;
867-
if(updateQueue!==null){
868-
processUpdateQueue(
869-
workInProgress,
870-
updateQueue,
871-
newProps,
872-
instance,
873-
renderExpirationTime,
874-
);
875-
instance.state=workInProgress.memoizedState;
876-
}
863+
processUpdateQueue(
864+
workInProgress,
865+
newProps,
866+
instance,
867+
renderExpirationTime,
868+
);
869+
instance.state=workInProgress.memoizedState;
877870
}
878871

879872
if(typeofinstance.componentDidMount==='function'){
@@ -936,17 +929,8 @@ function resumeMountClassInstance(
936929

937930
constoldState=workInProgress.memoizedState;
938931
letnewState=(instance.state=oldState);
939-
letupdateQueue=workInProgress.updateQueue;
940-
if(updateQueue!==null){
941-
processUpdateQueue(
942-
workInProgress,
943-
updateQueue,
944-
newProps,
945-
instance,
946-
renderExpirationTime,
947-
);
948-
newState=workInProgress.memoizedState;
949-
}
932+
processUpdateQueue(workInProgress,newProps,instance,renderExpirationTime);
933+
newState=workInProgress.memoizedState;
950934
if(
951935
oldProps===newProps&&
952936
oldState===newState&&
@@ -1035,6 +1019,8 @@ function updateClassInstance(
10351019
): boolean{
10361020
constinstance=workInProgress.stateNode;
10371021

1022+
cloneUpdateQueue(current,workInProgress);
1023+
10381024
constoldProps=workInProgress.memoizedProps;
10391025
instance.props=
10401026
workInProgress.type===workInProgress.elementType
@@ -1081,17 +1067,8 @@ function updateClassInstance(
10811067

10821068
constoldState=workInProgress.memoizedState;
10831069
letnewState=(instance.state=oldState);
1084-
letupdateQueue=workInProgress.updateQueue;
1085-
if(updateQueue!==null){
1086-
processUpdateQueue(
1087-
workInProgress,
1088-
updateQueue,
1089-
newProps,
1090-
instance,
1091-
renderExpirationTime,
1092-
);
1093-
newState=workInProgress.memoizedState;
1094-
}
1070+
processUpdateQueue(workInProgress,newProps,instance,renderExpirationTime);
1071+
newState=workInProgress.memoizedState;
10951072

10961073
if(
10971074
oldProps===newProps&&

0 commit comments

Comments
 (0)