Warn about incorrect use of useImperativeHandle() (#14647)
Dan Abramov committed
Jan 21, 2019 at 19:44 UTC
8f45a7fdc469c6d88ca5ca105095b03dd4e862e1
3 files changed
+66
packages/react-reconciler/src/ReactFiberHooks.js
+14
@@ -674,6 +674,12 @@ export function useImperativeHandle<T>(
674
): void {
675
if (__DEV__) {
676
currentHookNameInDev = 'useImperativeHandle';
677
+ warning(
678
+ typeof create === 'function',
679
+ 'Expected useImperativeHandle() second argument to be a function ' +
680
+ 'that creates a handle. Instead received: %s.',
681
+ create !== null ? typeof create : 'null',
682
+ );
683
}
684
// TODO: If deps are provided, should we skip comparing the ref itself?
685
const nextDeps =
@@ -690,6 +696,14 @@ export function useImperativeHandle<T>(
696
return () => refCallback(null);
697
} else if (ref !== null && ref !== undefined) {
698
const refObject = ref;
699
+ if (__DEV__) {
700
+ warning(
701
+ refObject.hasOwnProperty('current'),
702
+ 'Expected useImperativeHandle() first argument to either be a ' +
703
+ 'ref callback or React.createRef() object. Instead received: %s.',
704
+ 'an object with keys {' + Object.keys(refObject).join(', ') + '}',
705
+ );
706
+ }
707
const inst = create();
708
refObject.current = inst;
709
return () => {
packages/react-reconciler/src/ReactFiberScheduler.js
+13
@@ -489,6 +489,9 @@ function commitAllLifeCycles(
489
}
490
}
491
while (nextEffect !== null) {
492
+ if (__DEV__) {
493
+ setCurrentFiber(nextEffect);
494
+ }
495
const effectTag = nextEffect.effectTag;
496
497
if (effectTag & (Update | Callback)) {
@@ -513,6 +516,9 @@ function commitAllLifeCycles(
516
517
nextEffect = nextEffect.nextEffect;
518
}
519
+ if (__DEV__) {
520
+ resetCurrentFiber();
521
+ }
522
}
523
524
function commitPassiveEffects(root: FiberRoot, firstEffect: Fiber): void {
@@ -526,6 +532,10 @@ function commitPassiveEffects(root: FiberRoot, firstEffect: Fiber): void {
532
533
let effect = firstEffect;
534
do {
535
+ if (__DEV__) {
536
+ setCurrentFiber(effect);
537
+ }
538
+
539
if (effect.effectTag & Passive) {
540
let didError = false;
541
let error;
@@ -549,6 +559,9 @@ function commitPassiveEffects(root: FiberRoot, firstEffect: Fiber): void {
559
}
560
effect = effect.nextEffect;
561
} while (effect !== null);
562
+ if (__DEV__) {
563
+ resetCurrentFiber();
564
+ }
565
566
isRendering = previousIsRendering;
567
packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
+39
@@ -569,6 +569,45 @@ describe('ReactHooks', () => {
569
]);
570
});
571
572
+ it('warns for bad useImperativeHandle first arg', () => {
573
+ const {useImperativeHandle} = React;
574
+ function App() {
575
+ useImperativeHandle({
576
+ focus() {},
577
+ });
578
+ return null;
579
+ }
580
+
581
+ expect(() => {
582
+ expect(() => {
583
+ ReactTestRenderer.create(<App />);
584
+ }).toThrow('create is not a function');
585
+ }).toWarnDev([
586
+ 'Expected useImperativeHandle() first argument to either be a ' +
587
+ 'ref callback or React.createRef() object. ' +
588
+ 'Instead received: an object with keys {focus}.',
589
+ 'Expected useImperativeHandle() second argument to be a function ' +
590
+ 'that creates a handle. Instead received: undefined.',
591
+ ]);
592
+ });
593
+
594
+ it('warns for bad useImperativeHandle second arg', () => {
595
+ const {useImperativeHandle} = React;
596
+ const App = React.forwardRef((props, ref) => {
597
+ useImperativeHandle(ref, {
598
+ focus() {},
599
+ });
600
+ return null;
601
+ });
602
+
603
+ expect(() => {
604
+ ReactTestRenderer.create(<App />);
605
+ }).toWarnDev([
606
+ 'Expected useImperativeHandle() second argument to be a function ' +
607
+ 'that creates a handle. Instead received: object.',
608
+ ]);
609
+ });
610
+
611
// https://github.com/facebook/react/issues/14022
612
it('works with ReactDOMServer calls inside a component', () => {
613
const {useState} = React;