Skip to content

Commit ba07e4d

Browse files
committed
Render phase updates can now be extracted from the pending queue
1 parent f8244b2 commit ba07e4d

File tree

1 file changed

+57
-84
lines changed

1 file changed

+57
-84
lines changed

packages/react-reconciler/src/ReactFiberHooks.js

Lines changed: 57 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,8 @@ let workInProgressHook: Hook | null = null;
193193

194194
// Whether an update was scheduled during the currently executing render pass.
195195
let didScheduleRenderPhaseUpdate: boolean = false;
196-
// Lazily created map of render-phase updates
197-
let renderPhaseUpdates: Map<
198-
UpdateQueue<any, any>,
199-
Update<any, any>,
200-
> | null = null;
196+
// TODO: Move to custom dispatcher.
197+
let isRerender = false;
201198

202199
const RE_RENDER_LIMIT = 25;
203200

@@ -396,7 +393,6 @@ export function renderWithHooks(
396393
// workInProgressHook = null;
397394

398395
// didScheduleRenderPhaseUpdate = false;
399-
// renderPhaseUpdates = null;
400396

401397
// TODO Warn if no hooks are used at all during mount, then some are used during update.
402398
// Currently we will identify the update render as a mount because memoizedState === null.
@@ -428,6 +424,8 @@ export function renderWithHooks(
428424
let children = Component(props, refOrContext);
429425

430426
if (didScheduleRenderPhaseUpdate) {
427+
isRerender = true;
428+
431429
// Counter to prevent infinite loops.
432430
let numberOfReRenders: number = 0;
433431
do {
@@ -464,7 +462,7 @@ export function renderWithHooks(
464462
children = Component(props, refOrContext);
465463
} while (didScheduleRenderPhaseUpdate);
466464

467-
renderPhaseUpdates = null;
465+
isRerender = false;
468466
}
469467

470468
// We can assume the previous dispatcher is always this one, since we set it
@@ -494,7 +492,6 @@ export function renderWithHooks(
494492

495493
// These were reset above
496494
// didScheduleRenderPhaseUpdate = false;
497-
// renderPhaseUpdates = null;
498495

499496
invariant(
500497
!didRenderTooFewHooks,
@@ -540,7 +537,7 @@ export function resetHooks(): void {
540537
}
541538

542539
didScheduleRenderPhaseUpdate = false;
543-
renderPhaseUpdates = null;
540+
isRerender = false;
544541
}
545542

546543
function mountWorkInProgressHook(): Hook {
@@ -676,15 +673,17 @@ function updateReducer<S, I, A>(
676673

677674
queue.lastRenderedReducer = reducer;
678675

679-
if (renderPhaseUpdates !== null) {
676+
if (isRerender) {
680677
// This is a re-render. Apply the new render phase updates to the previous
681678
// work-in-progress hook.
682679
const dispatch: Dispatch<A> = (queue.dispatch: any);
683-
// Render phase updates are stored in a map of queue -> linked list
684-
const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
685-
if (firstRenderPhaseUpdate !== undefined) {
686-
renderPhaseUpdates.delete(queue);
687-
let newState = hook.memoizedState;
680+
const lastRenderPhaseUpdate = queue.pending;
681+
let newState = hook.memoizedState;
682+
if (lastRenderPhaseUpdate !== null) {
683+
// The queue doesn't persist past this render pass.
684+
queue.pending = null;
685+
686+
const firstRenderPhaseUpdate = lastRenderPhaseUpdate.next;
688687
let update = firstRenderPhaseUpdate;
689688
do {
690689
// Process this render phase update. We don't have to check the
@@ -693,7 +692,7 @@ function updateReducer<S, I, A>(
693692
const action = update.action;
694693
newState = reducer(newState, action);
695694
update = update.next;
696-
} while (update !== null);
695+
} while (update !== firstRenderPhaseUpdate);
697696

698697
// Mark that the fiber performed work, but only if the new state is
699698
// different from the current state.
@@ -711,10 +710,8 @@ function updateReducer<S, I, A>(
711710
}
712711

713712
queue.lastRenderedState = newState;
714-
715-
return [newState, dispatch];
716713
}
717-
return [hook.memoizedState, dispatch];
714+
return [newState, dispatch];
718715
}
719716

720717
const current: Hook = (currentHook: any);
@@ -1239,80 +1236,56 @@ function dispatchAction<S, A>(
12391236
);
12401237
}
12411238

1242-
const alternate = fiber.alternate;
1239+
const currentTime = requestCurrentTimeForUpdate();
1240+
const suspenseConfig = requestCurrentSuspenseConfig();
1241+
const expirationTime = computeExpirationForFiber(
1242+
currentTime,
1243+
fiber,
1244+
suspenseConfig,
1245+
);
1246+
1247+
const update: Update<S, A> = {
1248+
expirationTime,
1249+
suspenseConfig,
1250+
action,
1251+
eagerReducer: null,
1252+
eagerState: null,
1253+
next: (null: any),
1254+
};
1255+
1256+
if (__DEV__) {
1257+
update.priority = getCurrentPriorityLevel();
1258+
}
1259+
1260+
// Append the update to the end of the list.
1261+
const pending = queue.pending;
1262+
if (pending === null) {
1263+
// This is the first update. Create a circular list.
1264+
update.next = update;
1265+
} else {
1266+
const first = pending.next;
1267+
if (first !== null) {
1268+
// Still circular.
1269+
update.next = first;
1270+
}
1271+
pending.next = update;
1272+
}
1273+
queue.pending = update;
1274+
12431275
if (
1244-
fiber === currentlyRenderingFiber ||
1245-
(alternate !== null && alternate === currentlyRenderingFiber)
1276+
currentlyRenderingFiber !== null &&
1277+
(fiber === currentlyRenderingFiber ||
1278+
fiber.alternate === currentlyRenderingFiber)
12461279
) {
12471280
// This is a render phase update. Stash it in a lazily-created map of
12481281
// queue -> linked list of updates. After this render pass, we'll restart
12491282
// and apply the stashed updates on top of the work-in-progress hook.
12501283
didScheduleRenderPhaseUpdate = true;
1251-
const update: Update<S, A> = {
1252-
expirationTime: renderExpirationTime,
1253-
suspenseConfig: null,
1254-
action,
1255-
eagerReducer: null,
1256-
eagerState: null,
1257-
next: (null: any),
1258-
};
1259-
if (__DEV__) {
1260-
update.priority = getCurrentPriorityLevel();
1261-
}
1262-
if (renderPhaseUpdates === null) {
1263-
renderPhaseUpdates = new Map();
1264-
}
1265-
const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
1266-
if (firstRenderPhaseUpdate === undefined) {
1267-
renderPhaseUpdates.set(queue, update);
1268-
} else {
1269-
// Append the update to the end of the list.
1270-
let lastRenderPhaseUpdate = firstRenderPhaseUpdate;
1271-
while (lastRenderPhaseUpdate.next !== null) {
1272-
lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
1273-
}
1274-
lastRenderPhaseUpdate.next = update;
1275-
}
1284+
update.expirationTime = renderExpirationTime;
12761285
} else {
1277-
const currentTime = requestCurrentTimeForUpdate();
1278-
const suspenseConfig = requestCurrentSuspenseConfig();
1279-
const expirationTime = computeExpirationForFiber(
1280-
currentTime,
1281-
fiber,
1282-
suspenseConfig,
1283-
);
1284-
1285-
const update: Update<S, A> = {
1286-
expirationTime,
1287-
suspenseConfig,
1288-
action,
1289-
eagerReducer: null,
1290-
eagerState: null,
1291-
next: (null: any),
1292-
};
1293-
1294-
if (__DEV__) {
1295-
update.priority = getCurrentPriorityLevel();
1296-
}
1297-
1298-
// Append the update to the end of the list.
1299-
const pending = queue.pending;
1300-
if (pending === null) {
1301-
// This is the first update. Create a circular list.
1302-
update.next = update;
1303-
} else {
1304-
const first = pending.next;
1305-
if (first !== null) {
1306-
// Still circular.
1307-
update.next = first;
1308-
}
1309-
pending.next = update;
1310-
}
1311-
queue.pending = update;
1312-
13131286
if (
13141287
fiber.expirationTime === NoWork &&
1315-
(alternate === null || alternate.expirationTime === NoWork)
1288+
(fiber.alternate === null || fiber.alternate.expirationTime === NoWork)
13161289
) {
13171290
// The queue is currently empty, which means we can eagerly compute the
13181291
// next state before entering the render phase. If the new state is the

0 commit comments

Comments
 (0)