From b8ec1f87b16c67942292081f676cf7d51ca16dce Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 21 Jan 2019 20:33:29 +0000 Subject: [PATCH 1/9] Revert "Revert "Disallow reading context during useMemo etc" (#14651)" This reverts commit 5fce6488ce1dc97e31515a47ff409d32ab722d65. --- .../src/ReactFiberClassComponent.js | 9 +- .../src/ReactFiberDispatcher.js | 2 +- .../react-reconciler/src/ReactFiberHooks.js | 29 ++++++- .../src/__tests__/ReactHooks-test.internal.js | 82 +++++++++++++++++++ 4 files changed, 110 insertions(+), 12 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 16a3e39fc316b..3875371714f38 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -20,7 +20,6 @@ import { import ReactStrictModeWarnings from './ReactStrictModeWarnings'; import {isMounted} from 'react-reconciler/reflection'; import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap'; -import ReactSharedInternals from 'shared/ReactSharedInternals'; import shallowEqual from 'shared/shallowEqual'; import getComponentName from 'shared/getComponentName'; import invariant from 'shared/invariant'; @@ -48,6 +47,7 @@ import { hasContextChanged, emptyContextObject, } from './ReactFiberContext'; +import {readContext} from './ReactFiberNewContext'; import { requestCurrentTime, computeExpirationForFiber, @@ -55,13 +55,6 @@ import { flushPassiveEffects, } from './ReactFiberScheduler'; -const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; - -function readContext(contextType: any): any { - const dispatcher = ReactCurrentDispatcher.current; - return dispatcher.readContext(contextType); -} - const fakeInternalInstance = {}; const isArray = Array.isArray; diff --git a/packages/react-reconciler/src/ReactFiberDispatcher.js b/packages/react-reconciler/src/ReactFiberDispatcher.js index ed2ed3b2f315b..ad1f2b3e51c77 100644 --- a/packages/react-reconciler/src/ReactFiberDispatcher.js +++ b/packages/react-reconciler/src/ReactFiberDispatcher.js @@ -7,8 +7,8 @@ * @flow */ -import {readContext} from './ReactFiberNewContext'; import { + readContext, useCallback, useContext, useEffect, diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 4af03961690c5..b69046b95800a 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -14,7 +14,7 @@ import type {HookEffectTag} from './ReactHookEffectTags'; import {NoWork} from './ReactFiberExpirationTime'; import {enableHooks} from 'shared/ReactFeatureFlags'; -import {readContext} from './ReactFiberNewContext'; +import {readContext as readContextWithoutCheck} from './ReactFiberNewContext'; import { Update as UpdateEffect, Passive as PassiveEffect, @@ -284,7 +284,7 @@ export function resetHooks(): void { // This is used to reset the state of this module when a component throws. // It's also called inside mountIndeterminateComponent if we determine the - // component is a module-style component. + // component is a module-style component, and also in readContext() above. renderExpirationTime = NoWork; currentlyRenderingFiber = null; @@ -394,7 +394,7 @@ export function useContext( // Ensure we're in a function component (class components support only the // .unstable_read() form) resolveCurrentlyRenderingFiber(); - return readContext(context, observedBits); + return readContextWithoutCheck(context, observedBits); } export function useState( @@ -771,6 +771,29 @@ export function useMemo( return nextValue; } +export function readContext( + context: ReactContext, + observedBits: void | number | boolean, +): T { + // Forbid reading context inside Hooks. + // The outer check tells us whether we're inside a Hook like useMemo(). + // However, it would also be true if we're rendering a class. + if (currentlyRenderingFiber === null) { + // The inner check tells us we're currently in renderWithHooks() phase + // rather than, for example, in a class or a context consumer. + // Then we know it should be an error. + if (renderExpirationTime !== NoWork) { + invariant( + false, + 'Context can only be read inside the body of a component. ' + + 'If you read context inside a Hook like useMemo or useReducer, ' + + 'move the call directly into the component body.', + ); + } + } + return readContextWithoutCheck(context, observedBits); +} + function dispatchAction( fiber: Fiber, queue: UpdateQueue, diff --git a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js index d13755ebddb95..f8532a22cc4ec 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js @@ -633,6 +633,88 @@ describe('ReactHooks', () => { expect(root.toJSON()).toEqual('123'); }); + it('throws when reading context inside useMemo', () => { + const {useMemo, createContext} = React; + const ReactCurrentDispatcher = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED + .ReactCurrentDispatcher; + + const ThemeContext = createContext('light'); + function App() { + return useMemo(() => { + return ReactCurrentDispatcher.current.readContext(ThemeContext); + }, []); + } + + expect(() => ReactTestRenderer.create()).toThrow( + 'Context can only be read inside the body of a component', + ); + }); + + it('throws when reading context inside useEffect', () => { + const {useEffect, createContext} = React; + const ReactCurrentDispatcher = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED + .ReactCurrentDispatcher; + + const ThemeContext = createContext('light'); + function App() { + useEffect(() => { + ReactCurrentDispatcher.current.readContext(ThemeContext); + }); + return null; + } + + const root = ReactTestRenderer.create(); + expect(() => root.update()).toThrow( + // The exact message doesn't matter, just make sure we don't allow this + "Cannot read property 'readContext' of null", + ); + }); + + it('throws when reading context inside useLayoutEffect', () => { + const {useLayoutEffect, createContext} = React; + const ReactCurrentDispatcher = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED + .ReactCurrentDispatcher; + + const ThemeContext = createContext('light'); + function App() { + useLayoutEffect(() => { + ReactCurrentDispatcher.current.readContext(ThemeContext); + }); + return null; + } + + expect(() => ReactTestRenderer.create()).toThrow( + // The exact message doesn't matter, just make sure we don't allow this + "Cannot read property 'readContext' of null", + ); + }); + + it('throws when reading context inside useReducer', () => { + const {useReducer, createContext} = React; + const ReactCurrentDispatcher = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED + .ReactCurrentDispatcher; + + const ThemeContext = createContext('light'); + function App() { + useReducer( + () => { + ReactCurrentDispatcher.current.readContext(ThemeContext); + }, + null, + {}, + ); + return null; + } + + expect(() => ReactTestRenderer.create()).toThrow( + 'Context can only be read inside the body of a component.', + ); + }); + it('throws when calling hooks inside useReducer', () => { const {useReducer, useRef} = React; function App() { From 0ad7dc1a25aed8b9db4006b1af41d8c18c7eda62 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 21 Jan 2019 20:33:29 +0000 Subject: [PATCH 2/9] Revert "Add test coverage for readContext() on the server (#14649)" This reverts commit fe2ecd276e9b9e57a49ddf2d86ff01677b69b493. From 3c2411371dbffda44fc9a090895e1380ced32400 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 21 Jan 2019 20:33:29 +0000 Subject: [PATCH 3/9] Revert "Warn about incorrect use of useImperativeHandle() (#14647)" This reverts commit 8f45a7fdc469c6d88ca5ca105095b03dd4e862e1. From 2f6d5f458e45f3ac3eefc8ebc060866b8d90b902 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 21 Jan 2019 20:33:29 +0000 Subject: [PATCH 4/9] Revert "Disallow reading context during useMemo etc (#14648)" This reverts commit 1fcbd22431d97e404c1f684744dc543b6c22b076. --- .../src/ReactFiberClassComponent.js | 9 +- .../src/ReactFiberDispatcher.js | 2 +- .../react-reconciler/src/ReactFiberHooks.js | 29 +------ .../src/__tests__/ReactHooks-test.internal.js | 82 ------------------- 4 files changed, 12 insertions(+), 110 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 3875371714f38..16a3e39fc316b 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -20,6 +20,7 @@ import { import ReactStrictModeWarnings from './ReactStrictModeWarnings'; import {isMounted} from 'react-reconciler/reflection'; import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; import shallowEqual from 'shared/shallowEqual'; import getComponentName from 'shared/getComponentName'; import invariant from 'shared/invariant'; @@ -47,7 +48,6 @@ import { hasContextChanged, emptyContextObject, } from './ReactFiberContext'; -import {readContext} from './ReactFiberNewContext'; import { requestCurrentTime, computeExpirationForFiber, @@ -55,6 +55,13 @@ import { flushPassiveEffects, } from './ReactFiberScheduler'; +const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; + +function readContext(contextType: any): any { + const dispatcher = ReactCurrentDispatcher.current; + return dispatcher.readContext(contextType); +} + const fakeInternalInstance = {}; const isArray = Array.isArray; diff --git a/packages/react-reconciler/src/ReactFiberDispatcher.js b/packages/react-reconciler/src/ReactFiberDispatcher.js index ad1f2b3e51c77..ed2ed3b2f315b 100644 --- a/packages/react-reconciler/src/ReactFiberDispatcher.js +++ b/packages/react-reconciler/src/ReactFiberDispatcher.js @@ -7,8 +7,8 @@ * @flow */ +import {readContext} from './ReactFiberNewContext'; import { - readContext, useCallback, useContext, useEffect, diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index b69046b95800a..4af03961690c5 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -14,7 +14,7 @@ import type {HookEffectTag} from './ReactHookEffectTags'; import {NoWork} from './ReactFiberExpirationTime'; import {enableHooks} from 'shared/ReactFeatureFlags'; -import {readContext as readContextWithoutCheck} from './ReactFiberNewContext'; +import {readContext} from './ReactFiberNewContext'; import { Update as UpdateEffect, Passive as PassiveEffect, @@ -284,7 +284,7 @@ export function resetHooks(): void { // This is used to reset the state of this module when a component throws. // It's also called inside mountIndeterminateComponent if we determine the - // component is a module-style component, and also in readContext() above. + // component is a module-style component. renderExpirationTime = NoWork; currentlyRenderingFiber = null; @@ -394,7 +394,7 @@ export function useContext( // Ensure we're in a function component (class components support only the // .unstable_read() form) resolveCurrentlyRenderingFiber(); - return readContextWithoutCheck(context, observedBits); + return readContext(context, observedBits); } export function useState( @@ -771,29 +771,6 @@ export function useMemo( return nextValue; } -export function readContext( - context: ReactContext, - observedBits: void | number | boolean, -): T { - // Forbid reading context inside Hooks. - // The outer check tells us whether we're inside a Hook like useMemo(). - // However, it would also be true if we're rendering a class. - if (currentlyRenderingFiber === null) { - // The inner check tells us we're currently in renderWithHooks() phase - // rather than, for example, in a class or a context consumer. - // Then we know it should be an error. - if (renderExpirationTime !== NoWork) { - invariant( - false, - 'Context can only be read inside the body of a component. ' + - 'If you read context inside a Hook like useMemo or useReducer, ' + - 'move the call directly into the component body.', - ); - } - } - return readContextWithoutCheck(context, observedBits); -} - function dispatchAction( fiber: Fiber, queue: UpdateQueue, diff --git a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js index f8532a22cc4ec..d13755ebddb95 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js @@ -633,88 +633,6 @@ describe('ReactHooks', () => { expect(root.toJSON()).toEqual('123'); }); - it('throws when reading context inside useMemo', () => { - const {useMemo, createContext} = React; - const ReactCurrentDispatcher = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .ReactCurrentDispatcher; - - const ThemeContext = createContext('light'); - function App() { - return useMemo(() => { - return ReactCurrentDispatcher.current.readContext(ThemeContext); - }, []); - } - - expect(() => ReactTestRenderer.create()).toThrow( - 'Context can only be read inside the body of a component', - ); - }); - - it('throws when reading context inside useEffect', () => { - const {useEffect, createContext} = React; - const ReactCurrentDispatcher = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .ReactCurrentDispatcher; - - const ThemeContext = createContext('light'); - function App() { - useEffect(() => { - ReactCurrentDispatcher.current.readContext(ThemeContext); - }); - return null; - } - - const root = ReactTestRenderer.create(); - expect(() => root.update()).toThrow( - // The exact message doesn't matter, just make sure we don't allow this - "Cannot read property 'readContext' of null", - ); - }); - - it('throws when reading context inside useLayoutEffect', () => { - const {useLayoutEffect, createContext} = React; - const ReactCurrentDispatcher = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .ReactCurrentDispatcher; - - const ThemeContext = createContext('light'); - function App() { - useLayoutEffect(() => { - ReactCurrentDispatcher.current.readContext(ThemeContext); - }); - return null; - } - - expect(() => ReactTestRenderer.create()).toThrow( - // The exact message doesn't matter, just make sure we don't allow this - "Cannot read property 'readContext' of null", - ); - }); - - it('throws when reading context inside useReducer', () => { - const {useReducer, createContext} = React; - const ReactCurrentDispatcher = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .ReactCurrentDispatcher; - - const ThemeContext = createContext('light'); - function App() { - useReducer( - () => { - ReactCurrentDispatcher.current.readContext(ThemeContext); - }, - null, - {}, - ); - return null; - } - - expect(() => ReactTestRenderer.create()).toThrow( - 'Context can only be read inside the body of a component.', - ); - }); - it('throws when calling hooks inside useReducer', () => { const {useReducer, useRef} = React; function App() { From ae19d8ff14677403b1adfc00083fb68b651ce8e0 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 21 Jan 2019 20:33:29 +0000 Subject: [PATCH 5/9] Revert "Warn about refs on lazy function components (#14645)" This reverts commit 2a084f51a94df30e3bffd42bf3a04ce3da231512. From 396a45ab3e50ea4e8343888a6da6aada63394b66 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 21 Jan 2019 20:33:29 +0000 Subject: [PATCH 6/9] Revert "Fix typo (#14560)" This reverts commit b5a3df6e8894ebf2334179e445b0803f6819aa9f. From 59021c0d34bfe345d226bd0cc91539b9c73385e6 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 21 Jan 2019 20:33:29 +0000 Subject: [PATCH 7/9] Revert "fix typo (#14316)" This reverts commit 9c146e675174a523f90f630c87f62f55812bacea. From acb50852af4c4bafef514db8dc4d4c1ccd1e46d6 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 21 Jan 2019 20:33:29 +0000 Subject: [PATCH 8/9] Revert "Mention forwardRef() in errors and warnings (#14644)" This reverts commit baa6d40fc856c65e1d74ddf4fa038d7a65a787ed. From e4a8ff98c6a2bafdc96e04ede49f60cc31ee1f11 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 21 Jan 2019 20:33:29 +0000 Subject: [PATCH 9/9] Revert "Double-render function components with Hooks in DEV in StrictMode (#14643)" This reverts commit a1414e8949915c006bba411fa2949b304dd044ae. --- .../src/ReactFiberBeginWork.js | 53 ----- .../src/__tests__/ReactHooks-test.internal.js | 220 ------------------ 2 files changed, 273 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 8328b694f0724..a00b795b16dd4 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -250,23 +250,6 @@ function updateForwardRef( ref, renderExpirationTime, ); - if ( - debugRenderPhaseSideEffects || - (debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode) - ) { - // Only double-render components with Hooks - if (workInProgress.memoizedState !== null) { - renderWithHooks( - current, - workInProgress, - render, - nextProps, - ref, - renderExpirationTime, - ); - } - } setCurrentPhase(null); } else { nextChildren = renderWithHooks( @@ -560,23 +543,6 @@ function updateFunctionComponent( context, renderExpirationTime, ); - if ( - debugRenderPhaseSideEffects || - (debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode) - ) { - // Only double-render components with Hooks - if (workInProgress.memoizedState !== null) { - renderWithHooks( - current, - workInProgress, - Component, - nextProps, - context, - renderExpirationTime, - ); - } - } setCurrentPhase(null); } else { nextChildren = renderWithHooks( @@ -1241,25 +1207,6 @@ function mountIndeterminateComponent( } else { // Proceed under the assumption that this is a function component workInProgress.tag = FunctionComponent; - if (__DEV__) { - if ( - debugRenderPhaseSideEffects || - (debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode) - ) { - // Only double-render components with Hooks - if (workInProgress.memoizedState !== null) { - renderWithHooks( - null, - workInProgress, - Component, - props, - context, - renderExpirationTime, - ); - } - } - } reconcileChildren(null, workInProgress, value, renderExpirationTime); if (__DEV__) { validateFunctionComponentInDev(workInProgress, Component); diff --git a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js index d13755ebddb95..19fa96e8cdc19 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js @@ -660,224 +660,4 @@ describe('ReactHooks', () => { 'Hooks can only be called inside the body of a function component', ); }); - - it('double-invokes components with Hooks in Strict Mode', () => { - ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = true; - - const {useState, StrictMode} = React; - let renderCount = 0; - - function NoHooks() { - renderCount++; - return
; - } - - function HasHooks() { - useState(0); - renderCount++; - return
; - } - - const FwdRef = React.forwardRef((props, ref) => { - renderCount++; - return
; - }); - - const FwdRefHasHooks = React.forwardRef((props, ref) => { - useState(0); - renderCount++; - return
; - }); - - const Memo = React.memo(props => { - renderCount++; - return
; - }); - - const MemoHasHooks = React.memo(props => { - useState(0); - renderCount++; - return
; - }); - - function Factory() { - return { - state: {}, - render() { - renderCount++; - return
; - }, - }; - } - - let renderer = ReactTestRenderer.create(null); - - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(1); - - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(1); - - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(1); - - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(__DEV__ ? 2 : 1); // Treated like a class - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(__DEV__ ? 2 : 1); // Treated like a class - - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks - - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks - - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update(); - expect(renderCount).toBe(1); - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks - renderCount = 0; - renderer.update( - - - , - ); - expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks - }); - - it('double-invokes useMemo in DEV StrictMode despite []', () => { - ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = true; - const {useMemo, StrictMode} = React; - - let useMemoCount = 0; - function BadUseMemo() { - useMemo(() => { - useMemoCount++; - }, []); - return
; - } - - useMemoCount = 0; - ReactTestRenderer.create( - - - , - ); - expect(useMemoCount).toBe(__DEV__ ? 2 : 1); // Has Hooks - }); });