@@ -182,15 +182,10 @@ let currentlyRenderingFiber: Fiber = (null: any);
182
182
let currentHook : Hook | null = null ;
183
183
let workInProgressHook : Hook | null = null ;
184
184
185
- // Updates scheduled during render will trigger an immediate re-render at the
186
- // end of the current pass. We can't store these updates on the normal queue,
187
- // because if the work is aborted, they should be discarded. Because this is
188
- // a relatively rare case, we also don't want to add an additional field to
189
- // either the hook or queue object types. So we store them in a lazily create
190
- // map of queue -> render-phase updates, which are discarded once the component
191
- // completes without re-rendering.
192
-
193
- // Whether an update was scheduled during the currently executing render pass.
185
+ // Whether an update was scheduled at any point during the render phase. This
186
+ // does not get reset if we do another render pass; only when we're completely
187
+ // finished evaluating this component. This is an optimization so we know
188
+ // whether we need to clear render phase updates after a throw.
194
189
let didScheduleRenderPhaseUpdate : boolean = false ;
195
190
196
191
const RE_RENDER_LIMIT = 25 ;
@@ -416,11 +411,13 @@ export function renderWithHooks(
416
411
417
412
let children = Component ( props , refOrContext ) ;
418
413
419
- if ( didScheduleRenderPhaseUpdate ) {
420
- // Counter to prevent infinite loops.
414
+ // Check if there was a render phase update
415
+ if ( workInProgress . expirationTime === renderExpirationTime ) {
416
+ // Keep rendering in a loop for as long as render phase updates continue to
417
+ // be scheduled. Use a counter to prevent infinite loops.
421
418
let numberOfReRenders : number = 0 ;
422
419
do {
423
- didScheduleRenderPhaseUpdate = false ;
420
+ workInProgress . expirationTime = NoWork ;
424
421
425
422
invariant (
426
423
numberOfReRenders < RE_RENDER_LIMIT ,
@@ -451,7 +448,7 @@ export function renderWithHooks(
451
448
: HooksDispatcherOnRerender ;
452
449
453
450
children = Component ( props , refOrContext ) ;
454
- } while ( didScheduleRenderPhaseUpdate ) ;
451
+ } while ( workInProgress . expirationTime === renderExpirationTime ) ;
455
452
}
456
453
457
454
// We can assume the previous dispatcher is always this one, since we set it
@@ -479,8 +476,7 @@ export function renderWithHooks(
479
476
hookTypesUpdateIndexDev = - 1 ;
480
477
}
481
478
482
- // These were reset above
483
- // didScheduleRenderPhaseUpdate = false;
479
+ didScheduleRenderPhaseUpdate = false ;
484
480
485
481
invariant (
486
482
! didRenderTooFewHooks ,
@@ -509,23 +505,21 @@ export function resetHooksAfterThrow(): void {
509
505
ReactCurrentDispatcher . current = ContextOnlyDispatcher ;
510
506
511
507
if ( didScheduleRenderPhaseUpdate ) {
512
- const current = ( currentlyRenderingFiber : any ) . alternate ;
513
- if ( current !== null ) {
514
- // There were render phase updates. These are only valid for this render
515
- // pass, which we are now aborting. Remove the updates from the queues so
516
- // they do not persist to the next render. We already did a single pass
517
- // through the whole list of hooks, so we know that any pending updates
518
- // must have been dispatched during the render phase. The ones that were
519
- // dispatched before we started rendering were already transferred to the
520
- // current hook's queue.
521
- let hook : Hook | null = current . memoizedState ;
522
- while ( hook !== null ) {
523
- const queue = hook . queue ;
524
- if ( queue !== null ) {
525
- queue . pending = null ;
526
- }
527
- hook = hook . next ;
508
+ // There were render phase updates. These are only valid for this render
509
+ // phase, which we are now aborting. Remove the updates from the queues so
510
+ // they do not persist to the next render. Do not remove updates from hooks
511
+ // that weren't processed.
512
+ //
513
+ // Only reset the updates from the queue if it has a clone. If it does
514
+ // not have a clone, that means it wasn't processed, and the updates were
515
+ // scheduled before we entered the render phase.
516
+ let hook : Hook | null = currentlyRenderingFiber . memoizedState ;
517
+ while ( hook !== null ) {
518
+ const queue = hook . queue ;
519
+ if ( queue !== null ) {
520
+ queue . pending = null ;
528
521
}
522
+ hook = hook . next ;
529
523
}
530
524
}
531
525
@@ -1306,6 +1300,7 @@ function dispatchAction<S, A>(
1306
1300
// and apply the stashed updates on top of the work-in-progress hook.
1307
1301
didScheduleRenderPhaseUpdate = true ;
1308
1302
update . expirationTime = renderExpirationTime ;
1303
+ currentlyRenderingFiber . expirationTime = renderExpirationTime ;
1309
1304
} else {
1310
1305
if (
1311
1306
fiber . expirationTime === NoWork &&
0 commit comments