@@ -34,7 +34,8 @@ import {
34
34
} from './ReactFiberScheduler' ;
35
35
36
36
import invariant from 'shared/invariant' ;
37
- import areHookInputsEqual from 'shared/areHookInputsEqual' ;
37
+ import warning from 'shared/warning' ;
38
+ import is from 'shared/objectIs' ;
38
39
39
40
type Update < A > = {
40
41
expirationTime : ExpirationTime ,
@@ -109,6 +110,9 @@ let renderPhaseUpdates: Map<UpdateQueue<any>, Update<any>> | null = null;
109
110
let numberOfReRenders : number = 0 ;
110
111
const RE_RENDER_LIMIT = 25 ;
111
112
113
+ // In DEV, this is the name of the currently executing primitive hook
114
+ let currentHookNameInDev : ?string ;
115
+
112
116
function resolveCurrentlyRenderingFiber ( ) : Fiber {
113
117
invariant (
114
118
currentlyRenderingFiber !== null ,
@@ -117,6 +121,48 @@ function resolveCurrentlyRenderingFiber(): Fiber {
117
121
return currentlyRenderingFiber ;
118
122
}
119
123
124
+ function areHookInputsEqual (
125
+ nextDeps : Array < mixed > ,
126
+ prevDeps : Array < mixed > | null ,
127
+ ) {
128
+ if ( prevDeps === null ) {
129
+ if ( __DEV__ ) {
130
+ warning (
131
+ false ,
132
+ '%s received a final argument during this render, but not during ' +
133
+ 'the previous render. Even though the final argument is optional, ' +
134
+ 'its type cannot change between renders.' ,
135
+ currentHookNameInDev ,
136
+ ) ;
137
+ }
138
+ return false ;
139
+ }
140
+
141
+ if ( __DEV__ ) {
142
+ // Don't bother comparing lengths in prod because these arrays should be
143
+ // passed inline.
144
+ if ( nextDeps . length !== prevDeps . length ) {
145
+ warning (
146
+ false ,
147
+ 'The final argument passed to %s changed size between renders. The ' +
148
+ 'order and size of this array must remain constant.\n\n' +
149
+ 'Previous: %s\n' +
150
+ 'Incoming: %s' ,
151
+ currentHookNameInDev ,
152
+ `[${ nextDeps . join ( ', ' ) } ]` ,
153
+ `[${ prevDeps . join ( ', ' ) } ]` ,
154
+ ) ;
155
+ }
156
+ }
157
+ for ( let i = 0 ; i < nextDeps . length ; i ++ ) {
158
+ if ( is ( nextDeps [ i ] , prevDeps [ i ] ) ) {
159
+ continue ;
160
+ }
161
+ return false ;
162
+ }
163
+ return true ;
164
+ }
165
+
120
166
export function prepareToUseHooks (
121
167
current : Fiber | null ,
122
168
workInProgress : Fiber ,
@@ -193,6 +239,10 @@ export function finishHooks(
193
239
remainingExpirationTime = NoWork ;
194
240
componentUpdateQueue = null ;
195
241
242
+ if ( __DEV__ ) {
243
+ currentHookNameInDev = undefined ;
244
+ }
245
+
196
246
// Always set during createWorkInProgress
197
247
// isReRender = false;
198
248
@@ -229,6 +279,10 @@ export function resetHooks(): void {
229
279
remainingExpirationTime = NoWork ;
230
280
componentUpdateQueue = null ;
231
281
282
+ if ( __DEV__ ) {
283
+ currentHookNameInDev = undefined ;
284
+ }
285
+
232
286
// Always set during createWorkInProgress
233
287
// isReRender = false;
234
288
@@ -324,6 +378,9 @@ export function useContext<T>(
324
378
context: ReactContext< T > ,
325
379
observedBits: void | number | boolean,
326
380
): T {
381
+ if ( __DEV__ ) {
382
+ currentHookNameInDev = 'useContext' ;
383
+ }
327
384
// Ensure we're in a function component (class components support only the
328
385
// .unstable_read() form)
329
386
resolveCurrentlyRenderingFiber();
@@ -333,6 +390,9 @@ export function useContext<T>(
333
390
export function useState < S > (
334
391
initialState: (() => S ) | S ,
335
392
) : [ S , Dispatch < BasicStateAction < S > > ] {
393
+ if ( __DEV__ ) {
394
+ currentHookNameInDev = 'useState' ;
395
+ }
336
396
return useReducer(
337
397
basicStateReducer,
338
398
// useReducer has a special case to support lazy useState initializers
@@ -345,6 +405,11 @@ export function useReducer<S, A>(
345
405
initialState : S ,
346
406
initialAction : A | void | null ,
347
407
) : [ S , Dispatch < A > ] {
408
+ if ( __DEV__ ) {
409
+ if ( reducer !== basicStateReducer ) {
410
+ currentHookNameInDev = 'useReducer' ;
411
+ }
412
+ }
348
413
currentlyRenderingFiber = resolveCurrentlyRenderingFiber ( ) ;
349
414
workInProgressHook = createWorkInProgressHook ( ) ;
350
415
let queue : UpdateQueue < A > | null = (workInProgressHook.queue: any);
@@ -518,13 +583,19 @@ export function useLayoutEffect(
518
583
create : ( ) => mixed ,
519
584
deps : Array < mixed > | void | null,
520
585
): void {
586
+ if ( __DEV__ ) {
587
+ currentHookNameInDev = 'useLayoutEffect' ;
588
+ }
521
589
useEffectImpl(UpdateEffect, UnmountMutation | MountLayout, create, deps);
522
590
}
523
591
524
592
export function useEffect (
525
593
create : ( ) => mixed ,
526
594
deps : Array < mixed > | void | null,
527
595
): void {
596
+ if ( __DEV__ ) {
597
+ currentHookNameInDev = 'useEffect' ;
598
+ }
528
599
useEffectImpl(
529
600
UpdateEffect | PassiveEffect,
530
601
UnmountPassive | MountPassive,
@@ -542,11 +613,12 @@ function useEffectImpl(fiberEffectTag, hookEffectTag, create, deps): void {
542
613
if ( currentHook !== null ) {
543
614
const prevEffect = currentHook . memoizedState ;
544
615
destroy = prevEffect . destroy ;
545
- // Assume these are defined. If they're not, areHookInputsEqual will warn.
546
- const prevDeps : Array < mixed > = ( prevEffect . deps : any ) ;
547
- if ( nextDeps !== null && areHookInputsEqual ( nextDeps , prevDeps ) ) {
548
- pushEffect ( NoHookEffect , create , destroy , nextDeps ) ;
549
- return ;
616
+ if ( nextDeps !== null ) {
617
+ const prevDeps = prevEffect . deps ;
618
+ if ( areHookInputsEqual ( nextDeps , prevDeps ) ) {
619
+ pushEffect ( NoHookEffect , create , destroy , nextDeps ) ;
620
+ return ;
621
+ }
550
622
}
551
623
}
552
624
@@ -564,6 +636,9 @@ export function useImperativeHandle<T>(
564
636
create : ( ) = > T ,
565
637
deps : Array < mixed > | void | null ,
566
638
) : void {
639
+ if ( __DEV__ ) {
640
+ currentHookNameInDev = 'useImperativeHandle' ;
641
+ }
567
642
// TODO: If deps are provided, should we skip comparing the ref itself?
568
643
const nextDeps =
569
644
deps !== null && deps !== undefined ? deps . concat ( [ ref ] ) : [ ref ] ;
@@ -592,6 +667,9 @@ export function useDebugValue(
592
667
value : any ,
593
668
formatterFn : ?( value : any ) => any ,
594
669
) : void {
670
+ if ( __DEV__ ) {
671
+ currentHookNameInDev = 'useDebugValue' ;
672
+ }
595
673
// This hook is normally a no-op.
596
674
// The react-debug-hooks package injects its own implementation
597
675
// so that e.g. DevTools can display custom hook values.
@@ -601,17 +679,21 @@ export function useCallback<T>(
601
679
callback: T,
602
680
deps: Array< mixed > | void | null,
603
681
): T {
682
+ if ( __DEV__ ) {
683
+ currentHookNameInDev = 'useCallback' ;
684
+ }
604
685
currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
605
686
workInProgressHook = createWorkInProgressHook();
606
687
607
688
const nextDeps = deps === undefined ? null : deps;
608
689
609
690
const prevState = workInProgressHook.memoizedState;
610
691
if (prevState !== null) {
611
- // Assume these are defined. If they're not, areHookInputsEqual will warn.
612
- const prevDeps : Array < mixed > = prevState [ 1 ] ;
613
- if ( nextDeps !== null && areHookInputsEqual ( nextDeps , prevDeps ) ) {
614
- return prevState [ 0 ] ;
692
+ if ( nextDeps !== null ) {
693
+ const prevDeps : Array < mixed > | null = prevState [ 1 ] ;
694
+ if ( areHookInputsEqual ( nextDeps , prevDeps ) ) {
695
+ return prevState [ 0 ] ;
696
+ }
615
697
}
616
698
}
617
699
workInProgressHook . memoizedState = [ callback , nextDeps ] ;
@@ -622,6 +704,9 @@ export function useMemo<T>(
622
704
nextCreate: () => T ,
623
705
deps : Array < mixed > | void | null,
624
706
): T {
707
+ if ( __DEV__ ) {
708
+ currentHookNameInDev = 'useMemo' ;
709
+ }
625
710
currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
626
711
workInProgressHook = createWorkInProgressHook();
627
712
@@ -630,9 +715,11 @@ export function useMemo<T>(
630
715
const prevState = workInProgressHook.memoizedState;
631
716
if (prevState !== null) {
632
717
// Assume these are defined. If they're not, areHookInputsEqual will warn.
633
- const prevDeps = prevState [ 1 ] ;
634
- if ( nextDeps !== null && areHookInputsEqual ( nextDeps , prevDeps ) ) {
635
- return prevState [ 0 ] ;
718
+ if ( nextDeps !== null ) {
719
+ const prevDeps : Array < mixed > | null = prevState [ 1 ] ;
720
+ if ( areHookInputsEqual ( nextDeps , prevDeps ) ) {
721
+ return prevState [ 0 ] ;
722
+ }
636
723
}
637
724
}
638
725
0 commit comments