@@ -31,7 +31,7 @@ type Update<A> = {
31
31
} ;
32
32
33
33
type UpdateQueue < A > = {
34
- last : Update < A > | null ,
34
+ first : Update < A > | null ,
35
35
dispatch : any ,
36
36
} ;
37
37
@@ -196,7 +196,6 @@ class ReactShallowRenderer {
196
196
this . _isReRender = false ;
197
197
this . _didScheduleRenderPhaseUpdate = false ;
198
198
this . _renderPhaseUpdates = null ;
199
- this . _currentlyRenderingComponent = null ;
200
199
this . _numberOfReRenders = 0 ;
201
200
}
202
201
@@ -211,15 +210,14 @@ class ReactShallowRenderer {
211
210
_dispatcher: DispatcherType;
212
211
_workInProgressHook: null | Hook;
213
212
_firstWorkInProgressHook: null | Hook;
214
- _currentlyRenderingComponent: null | Object;
215
213
_renderPhaseUpdates: Map< UpdateQueue < any > , Update< any > > | null ;
216
214
_isReRender : boolean ;
217
215
_didScheduleRenderPhaseUpdate : boolean ;
218
216
_numberOfReRenders : number ;
219
217
220
218
_validateCurrentlyRenderingComponent ( ) {
221
219
invariant (
222
- this . _currentlyRenderingComponent !== null ,
220
+ this . _rendering && ! this . _instance ,
223
221
'Hooks can only be called inside the body of a function component. ' +
224
222
'(https://fb.me/react-invalid-hook-call)' ,
225
223
) ;
@@ -234,33 +232,44 @@ class ReactShallowRenderer {
234
232
this. _validateCurrentlyRenderingComponent ( ) ;
235
233
this . _createWorkInProgressHook ( ) ;
236
234
const workInProgressHook : Hook = ( this . _workInProgressHook : any ) ;
235
+
237
236
if ( this . _isReRender ) {
238
- // This is a re-render. Apply the new render phase updates to the previous
239
- // current hook.
237
+ // This is a re-render.
240
238
const queue : UpdateQueue < A > = ( workInProgressHook . queue : any ) ;
241
239
const dispatch : Dispatch < A > = ( queue . dispatch : any ) ;
242
- if ( this . _renderPhaseUpdates !== null ) {
243
- // Render phase updates are stored in a map of queue -> linked list
244
- const firstRenderPhaseUpdate = this . _renderPhaseUpdates . get ( queue ) ;
245
- if ( firstRenderPhaseUpdate !== undefined ) {
246
- ( this . _renderPhaseUpdates : any ) . delete ( queue ) ;
247
- let newState = workInProgressHook . memoizedState ;
248
- let update = firstRenderPhaseUpdate ;
249
- do {
250
- // Process this render phase update. We don't have to check the
251
- // priority because it will always be the same as the current
252
- // render's.
253
- const action = update . action ;
254
- newState = reducer ( newState , action ) ;
255
- update = update . next ;
256
- } while ( update !== null ) ;
257
-
258
- workInProgressHook . memoizedState = newState ;
259
-
260
- return [ newState , dispatch ] ;
240
+ if ( this . _numberOfReRenders > 0 ) {
241
+ // Apply the new render phase updates to the previous current hook.
242
+ if ( this . _renderPhaseUpdates !== null ) {
243
+ // Render phase updates are stored in a map of queue -> linked list
244
+ const firstRenderPhaseUpdate = this . _renderPhaseUpdates . get ( queue ) ;
245
+ if ( firstRenderPhaseUpdate !== undefined ) {
246
+ ( this . _renderPhaseUpdates : any ) . delete ( queue ) ;
247
+ let newState = workInProgressHook . memoizedState ;
248
+ let update = firstRenderPhaseUpdate ;
249
+ do {
250
+ const action = update . action ;
251
+ newState = reducer ( newState , action ) ;
252
+ update = update . next ;
253
+ } while ( update !== null ) ;
254
+ workInProgressHook . memoizedState = newState ;
255
+ return [ newState , dispatch ] ;
256
+ }
261
257
}
258
+ return [ workInProgressHook . memoizedState , dispatch ] ;
262
259
}
263
- return [ workInProgressHook . memoizedState , dispatch ] ;
260
+ // Process updates outside of render
261
+ let newState = workInProgressHook . memoizedState ;
262
+ let update = queue . first ;
263
+ if ( update !== null ) {
264
+ do {
265
+ const action = update . action ;
266
+ newState = reducer ( newState , action ) ;
267
+ update = update . next ;
268
+ } while ( update !== null ) ;
269
+ queue . first = null ;
270
+ workInProgressHook . memoizedState = newState ;
271
+ }
272
+ return [ newState , dispatch ] ;
264
273
} else {
265
274
let initialState ;
266
275
if ( reducer === basicStateReducer ) {
@@ -275,16 +284,12 @@ class ReactShallowRenderer {
275
284
}
276
285
workInProgressHook.memoizedState = initialState;
277
286
const queue: UpdateQueue< A > = (workInProgressHook.queue = {
278
- last : null ,
287
+ first : null ,
279
288
dispatch : null ,
280
289
} );
281
290
const dispatch: Dispatch<
282
291
A ,
283
- > = (queue.dispatch = (this._dispatchAction.bind(
284
- this,
285
- (this._currentlyRenderingComponent: any),
286
- queue,
287
- ): any));
292
+ > = (queue.dispatch = (this._dispatchAction.bind(this, queue): any));
288
293
return [workInProgressHook.memoizedState, dispatch];
289
294
}
290
295
} ;
@@ -375,18 +380,14 @@ class ReactShallowRenderer {
375
380
} ;
376
381
}
377
382
378
- _dispatchAction < A > (
379
- componentIdentity: Object,
380
- queue: UpdateQueue< A > ,
381
- action: A,
382
- ) {
383
+ _dispatchAction < A > (queue: UpdateQueue< A > , action: A) {
383
384
invariant (
384
385
this . _numberOfReRenders < RE_RENDER_LIMIT ,
385
386
'Too many re-renders. React limits the number of renders to prevent ' +
386
387
'an infinite loop.' ,
387
388
) ;
388
389
389
- if ( componentIdentity === this . _currentlyRenderingComponent ) {
390
+ if ( this . _rendering ) {
390
391
// This is a render phase update. Stash it in a lazily-created map of
391
392
// queue -> linked list of updates. After this render pass, we'll restart
392
393
// and apply the stashed updates on top of the work-in-progress hook.
@@ -411,9 +412,24 @@ class ReactShallowRenderer {
411
412
lastRenderPhaseUpdate . next = update ;
412
413
}
413
414
} else {
414
- // This means an update has happened after the function component has
415
- // returned. On the server this is a no-op. In React Fiber, the update
416
- // would be scheduled for a future render.
415
+ const update : Update < A > = {
416
+ action,
417
+ next : null ,
418
+ } ;
419
+
420
+ // Append the update to the end of the list.
421
+ let last = queue . first ;
422
+ if ( last === null ) {
423
+ queue . first = update ;
424
+ } else {
425
+ while ( last . next !== null ) {
426
+ last = last . next ;
427
+ }
428
+ last.next = update;
429
+ }
430
+
431
+ // Re-render now.
432
+ this.render(this._element, this._context);
417
433
}
418
434
}
419
435
@@ -443,10 +459,6 @@ class ReactShallowRenderer {
443
459
return this . _workInProgressHook ;
444
460
}
445
461
446
- _prepareToUseHooks(componentIdentity: Object): void {
447
- this . _currentlyRenderingComponent = componentIdentity ;
448
- }
449
-
450
462
_finishHooks ( element : ReactElement , context : null | Object ) {
451
463
if ( this . _didScheduleRenderPhaseUpdate ) {
452
464
// Updates were scheduled during the render phase. They are stored in
@@ -461,7 +473,6 @@ class ReactShallowRenderer {
461
473
this . _rendering = false ;
462
474
this . render ( element , context ) ;
463
475
} else {
464
- this . _currentlyRenderingComponent = null ;
465
476
this . _workInProgressHook = null ;
466
477
this . _renderPhaseUpdates = null ;
467
478
this . _numberOfReRenders = 0 ;
@@ -560,8 +571,6 @@ class ReactShallowRenderer {
560
571
} else {
561
572
const prevDispatcher = ReactCurrentDispatcher . current ;
562
573
ReactCurrentDispatcher . current = this . _dispatcher ;
563
- const componentIdentity = { } ;
564
- this . _prepareToUseHooks ( componentIdentity ) ;
565
574
try {
566
575
if ( isForwardRef ( element ) ) {
567
576
this . _rendered = element . type . render ( element . props , element . ref ) ;
0 commit comments