From 58e8304483ebfadd02a295339b5e9a989ac98c6e Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Tue, 19 Jan 2021 16:20:22 -0600 Subject: [PATCH] Remove custom error message from hook access error (#20604) It will still result in a null access error, so there's no change in semantics. We will print a user-friendly error message in DEV. --- .../__tests__/ReactHooksInspection-test.js | 7 +++-- .../ReactHooksWithNoopRenderer-test.js | 7 ++++- packages/react/src/ReactHooks.js | 29 +++++++++++-------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js index 602fb613d417f..50eadbedcbe46 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js @@ -277,14 +277,17 @@ describe('ReactHooksInspection', () => { }; expect(() => { - ReactDebugTools.inspectHooks(Foo, {}, FakeDispatcherRef); - }).toThrow( + expect(() => { + ReactDebugTools.inspectHooks(Foo, {}, FakeDispatcherRef); + }).toThrow("Cannot read property 'useState' of null"); + }).toErrorDev( 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.', + {withoutStack: true}, ); expect(getterCalls).toBe(1); diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js index 8d006b2f300bc..489928f27d208 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js @@ -249,13 +249,18 @@ describe('ReactHooksWithNoopRenderer', () => { } it('throws when called outside the render phase', () => { - expect(() => useState(0)).toThrow( + expect(() => { + expect(() => useState(0)).toThrow( + "Cannot read property 'useState' of null", + ); + }).toErrorDev( 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.', + {withoutStack: true}, ); }); diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index c1602a9bb53ed..d397d8f789f0a 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -7,6 +7,7 @@ * @flow */ +import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes'; import type { MutableSource, MutableSourceGetSnapshotFn, @@ -15,8 +16,6 @@ import type { } from 'shared/ReactTypes'; import type {OpaqueIDType} from 'react-reconciler/src/ReactFiberHostConfig'; -import invariant from 'shared/invariant'; - import ReactCurrentDispatcher from './ReactCurrentDispatcher'; type BasicStateAction = (S => S) | S; @@ -24,16 +23,22 @@ type Dispatch = A => void; function resolveDispatcher() { const dispatcher = ReactCurrentDispatcher.current; - invariant( - dispatcher !== null, - 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + - ' one of the following reasons:\n' + - '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + - '2. You might be breaking the Rules of Hooks\n' + - '3. You might have more than one copy of React in the same app\n' + - 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.', - ); - return dispatcher; + if (__DEV__) { + if (dispatcher === null) { + console.error( + 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + + ' one of the following reasons:\n' + + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + + '2. You might be breaking the Rules of Hooks\n' + + '3. You might have more than one copy of React in the same app\n' + + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.', + ); + } + } + // Will result in a null access error if accessed outside render phase. We + // intentionally don't throw our own error because this is in a hot path. + // Also helps ensure this is inlined. + return ((dispatcher: any): Dispatcher); } export function getCacheForType(resourceType: () => T): T {