Skip to content

Commit fd557d4

Browse files
authored
Warn on mount when deps are not an array (#15018)
* Warn on mount when deps are not an array * Check other Hooks * I can't figure out how to fix error/warning nesting lint But it doesn't really matter much because we test other cases in the other test.
1 parent ff596e3 commit fd557d4

2 files changed

Lines changed: 92 additions & 0 deletions

File tree

‎packages/react-reconciler/src/ReactFiberHooks.js‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,22 @@ function updateHookTypesDev() {
209209
}
210210
}
211211

212+
functioncheckDepsAreArrayDev(deps: mixed){
213+
if(__DEV__){
214+
if(deps!==undefined&&deps!==null&&!Array.isArray(deps)){
215+
// Verify deps, but only on mount to avoid extra checks.
216+
// It's unlikely their type would change as usually you define them inline.
217+
warning(
218+
false,
219+
'%s received a final argument that is not an array (instead, received `%s`). When '+
220+
'specified, the final argument must be an array.',
221+
currentHookNameInDev,
222+
typeofdeps,
223+
);
224+
}
225+
}
226+
}
227+
212228
functionwarnOnHookMismatchInDev(currentHookName: HookType){
213229
if(__DEV__){
214230
constcomponentName=getComponentName(
@@ -1249,6 +1265,7 @@ if (__DEV__) {
12491265
useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
12501266
currentHookNameInDev='useCallback';
12511267
mountHookTypesDev();
1268+
checkDepsAreArrayDev(deps);
12521269
returnmountCallback(callback,deps);
12531270
},
12541271
useContext<T>(
@@ -1265,6 +1282,7 @@ if (__DEV__) {
12651282
): void {
12661283
currentHookNameInDev='useEffect';
12671284
mountHookTypesDev();
1285+
checkDepsAreArrayDev(deps);
12681286
returnmountEffect(create,deps);
12691287
},
12701288
useImperativeHandle<T>(
@@ -1274,6 +1292,7 @@ if (__DEV__) {
12741292
): void {
12751293
currentHookNameInDev='useImperativeHandle';
12761294
mountHookTypesDev();
1295+
checkDepsAreArrayDev(deps);
12771296
returnmountImperativeHandle(ref,create,deps);
12781297
},
12791298
useLayoutEffect(
@@ -1282,11 +1301,13 @@ if (__DEV__) {
12821301
): void {
12831302
currentHookNameInDev='useLayoutEffect';
12841303
mountHookTypesDev();
1304+
checkDepsAreArrayDev(deps);
12851305
returnmountLayoutEffect(create,deps);
12861306
},
12871307
useMemo<T>(create: () =>T,deps: Array<mixed> | void | null): T {
12881308
currentHookNameInDev='useMemo';
12891309
mountHookTypesDev();
1310+
checkDepsAreArrayDev(deps);
12901311
constprevDispatcher=ReactCurrentDispatcher.current;
12911312
ReactCurrentDispatcher.current=InvalidNestedHooksDispatcherOnMountInDEV;
12921313
try{

‎packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,77 @@ describe('ReactHooks', () => {
632632
]);
633633
});
634634

635+
it('warns if deps is not an array',()=>{
636+
const{useEffect, useLayoutEffect, useMemo, useCallback}=React;
637+
638+
functionApp(props){
639+
useEffect(()=>{},props.deps);
640+
useLayoutEffect(()=>{},props.deps);
641+
useMemo(()=>{},props.deps);
642+
useCallback(()=>{},props.deps);
643+
returnnull;
644+
}
645+
646+
expect(()=>{
647+
ReactTestRenderer.create(<Appdeps={'hello'}/>);
648+
}).toWarnDev([
649+
'Warning: useEffect received a final argument that is not an array (instead, received `string`). '+
650+
'When specified, the final argument must be an array.',
651+
'Warning: useLayoutEffect received a final argument that is not an array (instead, received `string`). '+
652+
'When specified, the final argument must be an array.',
653+
'Warning: useMemo received a final argument that is not an array (instead, received `string`). '+
654+
'When specified, the final argument must be an array.',
655+
'Warning: useCallback received a final argument that is not an array (instead, received `string`). '+
656+
'When specified, the final argument must be an array.',
657+
]);
658+
expect(()=>{
659+
ReactTestRenderer.create(<Appdeps={100500}/>);
660+
}).toWarnDev([
661+
'Warning: useEffect received a final argument that is not an array (instead, received `number`). '+
662+
'When specified, the final argument must be an array.',
663+
'Warning: useLayoutEffect received a final argument that is not an array (instead, received `number`). '+
664+
'When specified, the final argument must be an array.',
665+
'Warning: useMemo received a final argument that is not an array (instead, received `number`). '+
666+
'When specified, the final argument must be an array.',
667+
'Warning: useCallback received a final argument that is not an array (instead, received `number`). '+
668+
'When specified, the final argument must be an array.',
669+
]);
670+
expect(()=>{
671+
ReactTestRenderer.create(<Appdeps={{}}/>);
672+
}).toWarnDev([
673+
'Warning: useEffect received a final argument that is not an array (instead, received `object`). '+
674+
'When specified, the final argument must be an array.',
675+
'Warning: useLayoutEffect received a final argument that is not an array (instead, received `object`). '+
676+
'When specified, the final argument must be an array.',
677+
'Warning: useMemo received a final argument that is not an array (instead, received `object`). '+
678+
'When specified, the final argument must be an array.',
679+
'Warning: useCallback received a final argument that is not an array (instead, received `object`). '+
680+
'When specified, the final argument must be an array.',
681+
]);
682+
ReactTestRenderer.create(<Appdeps={[]}/>);
683+
ReactTestRenderer.create(<Appdeps={null}/>);
684+
ReactTestRenderer.create(<Appdeps={undefined}/>);
685+
});
686+
687+
it('warns if deps is not an array for useImperativeHandle',()=>{
688+
const{useImperativeHandle}=React;
689+
690+
constApp=React.forwardRef((props,ref)=>{
691+
useImperativeHandle(ref,()=>{},props.deps);
692+
returnnull;
693+
});
694+
695+
expect(()=>{
696+
ReactTestRenderer.create(<Appdeps={'hello'}/>);
697+
}).toWarnDev([
698+
'Warning: useImperativeHandle received a final argument that is not an array (instead, received `string`). '+
699+
'When specified, the final argument must be an array.',
700+
]);
701+
ReactTestRenderer.create(<Appdeps={[]}/>);
702+
ReactTestRenderer.create(<Appdeps={null}/>);
703+
ReactTestRenderer.create(<Appdeps={undefined}/>);
704+
});
705+
635706
it('assumes useEffect clean-up function is either a function or undefined',()=>{
636707
const{useLayoutEffect}=React;
637708

0 commit comments

Comments
 (0)