Skip to content

Commit f8244b2

Browse files
committed
Refactor Hooks update queue
1 parent 24e2806 commit f8244b2

File tree

2 files changed

+78
-54
lines changed

2 files changed

+78
-54
lines changed

packages/react-reconciler/src/ReactFiberHooks.js

+70-45
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type {ReactPriorityLevel} from './SchedulerWithReactIntegration';
2020

2121
import ReactSharedInternals from 'shared/ReactSharedInternals';
2222

23-
import {NoWork} from './ReactFiberExpirationTime';
23+
import {NoWork, Sync} from './ReactFiberExpirationTime';
2424
import {readContext} from './ReactFiberNewContext';
2525
import {createResponderListener} from './ReactFiberEvents';
2626
import {
@@ -108,13 +108,13 @@ type Update<S, A> = {
108108
action: A,
109109
eagerReducer: ((S, A) => S) | null,
110110
eagerState: S | null,
111-
next: Update<S, A> | null,
111+
next: Update<S, A>,
112112

113113
priority?: ReactPriorityLevel,
114114
};
115115

116116
type UpdateQueue<S, A> = {
117-
last: Update<S, A> | null,
117+
pending: Update<S, A> | null,
118118
dispatch: (A => mixed) | null,
119119
lastRenderedReducer: ((S, A) => S) | null,
120120
lastRenderedState: S | null,
@@ -144,7 +144,7 @@ export type Hook = {
144144
memoizedState: any,
145145

146146
baseState: any,
147-
baseUpdate: Update<any, any> | null,
147+
baseQueue: Update<any, any> | null,
148148
queue: UpdateQueue<any, any> | null,
149149

150150
next: Hook | null,
@@ -548,8 +548,8 @@ function mountWorkInProgressHook(): Hook {
548548
memoizedState: null,
549549

550550
baseState: null,
551+
baseQueue: null,
551552
queue: null,
552-
baseUpdate: null,
553553

554554
next: null,
555555
};
@@ -608,8 +608,8 @@ function updateWorkInProgressHook(): Hook {
608608
memoizedState: currentHook.memoizedState,
609609

610610
baseState: currentHook.baseState,
611+
baseQueue: currentHook.baseQueue,
611612
queue: currentHook.queue,
612-
baseUpdate: currentHook.baseUpdate,
613613

614614
next: null,
615615
};
@@ -649,7 +649,7 @@ function mountReducer<S, I, A>(
649649
}
650650
hook.memoizedState = hook.baseState = initialState;
651651
const queue = (hook.queue = {
652-
last: null,
652+
pending: null,
653653
dispatch: null,
654654
lastRenderedReducer: reducer,
655655
lastRenderedState: (initialState: any),
@@ -706,7 +706,7 @@ function updateReducer<S, I, A>(
706706
// the base state unless the queue is empty.
707707
// TODO: Not sure if this is the desired semantics, but it's what we
708708
// do for gDSFP. I can't remember why.
709-
if (hook.baseUpdate === queue.last) {
709+
if (hook.baseQueue === null) {
710710
hook.baseState = newState;
711711
}
712712

@@ -717,42 +717,55 @@ function updateReducer<S, I, A>(
717717
return [hook.memoizedState, dispatch];
718718
}
719719

720-
// The last update in the entire queue
721-
const last = queue.last;
722-
// The last update that is part of the base state.
723-
const baseUpdate = hook.baseUpdate;
724-
const baseState = hook.baseState;
725-
726-
// Find the first unprocessed update.
727-
let first;
728-
if (baseUpdate !== null) {
729-
if (last !== null) {
730-
// For the first update, the queue is a circular linked list where
731-
// `queue.last.next = queue.first`. Once the first update commits, and
732-
// the `baseUpdate` is no longer empty, we can unravel the list.
733-
last.next = null;
720+
const current: Hook = (currentHook: any);
721+
722+
// The last rebase update that is NOT part of the base state.
723+
let baseQueue = current.baseQueue;
724+
725+
// The last pending update that hasn't been processed yet.
726+
let pendingQueue = queue.pending;
727+
if (pendingQueue !== null) {
728+
// We have new updates that haven't been processed yet.
729+
// We'll add them to the base queue.
730+
if (baseQueue !== null) {
731+
// Merge the pending queue and the base queue.
732+
let baseFirst = baseQueue.next;
733+
let pendingFirst = pendingQueue.next;
734+
baseQueue.next = pendingFirst;
735+
pendingQueue.next = baseFirst;
734736
}
735-
first = baseUpdate.next;
736-
} else {
737-
first = last !== null ? last.next : null;
737+
current.baseQueue = baseQueue = pendingQueue;
738+
queue.pending = null;
738739
}
739-
if (first !== null) {
740-
let newState = baseState;
740+
741+
if (baseQueue !== null) {
742+
// We have a queue to process.
743+
let first = baseQueue.next;
744+
let newState = current.baseState;
745+
741746
let newBaseState = null;
742-
let newBaseUpdate = null;
743-
let prevUpdate = baseUpdate;
747+
let newBaseQueueFirst = null;
748+
let newBaseQueueLast = null;
744749
let update = first;
745-
let didSkip = false;
746750
do {
747751
const updateExpirationTime = update.expirationTime;
748752
if (updateExpirationTime < renderExpirationTime) {
749753
// Priority is insufficient. Skip this update. If this is the first
750754
// skipped update, the previous update/state is the new base
751755
// update/state.
752-
if (!didSkip) {
753-
didSkip = true;
754-
newBaseUpdate = prevUpdate;
756+
const clone: Update<S, A> = {
757+
expirationTime: update.expirationTime,
758+
suspenseConfig: update.suspenseConfig,
759+
action: update.action,
760+
eagerReducer: update.eagerReducer,
761+
eagerState: update.eagerState,
762+
next: (null: any),
763+
};
764+
if (newBaseQueueLast === null) {
765+
newBaseQueueFirst = newBaseQueueLast = clone;
755766
newBaseState = newState;
767+
} else {
768+
newBaseQueueLast = newBaseQueueLast.next = clone;
756769
}
757770
// Update the remaining priority in the queue.
758771
if (updateExpirationTime > currentlyRenderingFiber.expirationTime) {
@@ -762,6 +775,18 @@ function updateReducer<S, I, A>(
762775
} else {
763776
// This update does have sufficient priority.
764777

778+
if (newBaseQueueLast !== null) {
779+
const clone: Update<S, A> = {
780+
expirationTime: Sync, // This update is going to be committed so we never want uncommit it.
781+
suspenseConfig: update.suspenseConfig,
782+
action: update.action,
783+
eagerReducer: update.eagerReducer,
784+
eagerState: update.eagerState,
785+
next: (null: any),
786+
};
787+
newBaseQueueLast = newBaseQueueLast.next = clone;
788+
}
789+
765790
// Mark the event time of this update as relevant to this render pass.
766791
// TODO: This should ideally use the true event time of this update rather than
767792
// its priority which is a derived and not reverseable value.
@@ -783,13 +808,13 @@ function updateReducer<S, I, A>(
783808
newState = reducer(newState, action);
784809
}
785810
}
786-
prevUpdate = update;
787811
update = update.next;
788812
} while (update !== null && update !== first);
789813

790-
if (!didSkip) {
791-
newBaseUpdate = prevUpdate;
814+
if (newBaseQueueLast === null) {
792815
newBaseState = newState;
816+
} else {
817+
newBaseQueueLast.next = (newBaseQueueFirst: any);
793818
}
794819

795820
// Mark that the fiber performed work, but only if the new state is
@@ -799,8 +824,8 @@ function updateReducer<S, I, A>(
799824
}
800825

801826
hook.memoizedState = newState;
802-
hook.baseUpdate = newBaseUpdate;
803827
hook.baseState = newBaseState;
828+
hook.baseQueue = newBaseQueueLast;
804829

805830
queue.lastRenderedState = newState;
806831
}
@@ -818,7 +843,7 @@ function mountState<S>(
818843
}
819844
hook.memoizedState = hook.baseState = initialState;
820845
const queue = (hook.queue = {
821-
last: null,
846+
pending: null,
822847
dispatch: null,
823848
lastRenderedReducer: basicStateReducer,
824849
lastRenderedState: (initialState: any),
@@ -1229,7 +1254,7 @@ function dispatchAction<S, A>(
12291254
action,
12301255
eagerReducer: null,
12311256
eagerState: null,
1232-
next: null,
1257+
next: (null: any),
12331258
};
12341259
if (__DEV__) {
12351260
update.priority = getCurrentPriorityLevel();
@@ -1263,27 +1288,27 @@ function dispatchAction<S, A>(
12631288
action,
12641289
eagerReducer: null,
12651290
eagerState: null,
1266-
next: null,
1291+
next: (null: any),
12671292
};
12681293

12691294
if (__DEV__) {
12701295
update.priority = getCurrentPriorityLevel();
12711296
}
12721297

12731298
// Append the update to the end of the list.
1274-
const last = queue.last;
1275-
if (last === null) {
1299+
const pending = queue.pending;
1300+
if (pending === null) {
12761301
// This is the first update. Create a circular list.
12771302
update.next = update;
12781303
} else {
1279-
const first = last.next;
1304+
const first = pending.next;
12801305
if (first !== null) {
12811306
// Still circular.
12821307
update.next = first;
12831308
}
1284-
last.next = update;
1309+
pending.next = update;
12851310
}
1286-
queue.last = update;
1311+
queue.pending = update;
12871312

12881313
if (
12891314
fiber.expirationTime === NoWork &&

packages/react-reconciler/src/ReactFiberWorkLoop.js

+8-9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {ReactPriorityLevel} from './SchedulerWithReactIntegration';
1414
import type {Interaction} from 'scheduler/src/Tracing';
1515
import type {SuspenseConfig} from './ReactFiberSuspenseConfig';
1616
import type {SuspenseState} from './ReactFiberSuspenseComponent';
17+
import type {Hook} from './ReactFiberHooks';
1718

1819
import {
1920
warnAboutDeprecatedLifecycles,
@@ -2881,12 +2882,11 @@ export function checkForWrongSuspensePriorityInDEV(sourceFiber: Fiber) {
28812882
break;
28822883
case FunctionComponent:
28832884
case ForwardRef:
2884-
case SimpleMemoComponent:
2885-
if (
2886-
workInProgressNode.memoizedState !== null &&
2887-
workInProgressNode.memoizedState.baseUpdate !== null
2888-
) {
2889-
let update = workInProgressNode.memoizedState.baseUpdate;
2885+
case SimpleMemoComponent: {
2886+
let firstHook: null | Hook = current.memoizedState;
2887+
// TODO: This just checks the first Hook. Isn't it suppose to check all Hooks?
2888+
if (firstHook !== null && firstHook.baseQueue !== null) {
2889+
let update = firstHook.baseQueue;
28902890
// Loop through the functional component's memoized state to see whether
28912891
// the component has triggered any high pri updates
28922892
while (update !== null) {
@@ -2906,15 +2906,14 @@ export function checkForWrongSuspensePriorityInDEV(sourceFiber: Fiber) {
29062906
}
29072907
break;
29082908
}
2909-
if (
2910-
update.next === workInProgressNode.memoizedState.baseUpdate
2911-
) {
2909+
if (update.next === firstHook.baseQueue) {
29122910
break;
29132911
}
29142912
update = update.next;
29152913
}
29162914
}
29172915
break;
2916+
}
29182917
default:
29192918
break;
29202919
}

0 commit comments

Comments
 (0)