diff --git a/src/hooks/useReduxContext.ts b/src/hooks/useReduxContext.ts index 61b009e9f..1e0f3887b 100644 --- a/src/hooks/useReduxContext.ts +++ b/src/hooks/useReduxContext.ts @@ -2,6 +2,27 @@ import { useContext } from 'react' import { ReactReduxContext } from '../components/Context' import type { ReactReduxContextValue } from '../components/Context' +/** + * Hook factory, which creates a `useReduxContext` hook bound to a given context. This is a low-level + * hook that you should usually not need to call directly. + * + * @param {React.Context} [context=ReactReduxContext] Context passed to your ``. + * @returns {Function} A `useReduxContext` hook bound to the specified context. + */ +export function createReduxContextHook(context = ReactReduxContext) { + return function useReduxContext(): ReactReduxContextValue | null { + const contextValue = useContext(context) + + if (process.env.NODE_ENV !== 'production' && !contextValue) { + throw new Error( + 'could not find react-redux context value; please ensure the component is wrapped in a ' + ) + } + + return contextValue + } +} + /** * A hook to access the value of the `ReactReduxContext`. This is a low-level * hook that you should usually not need to call directly. @@ -18,14 +39,4 @@ import type { ReactReduxContextValue } from '../components/Context' * return
{store.getState()}
* } */ -export function useReduxContext(): ReactReduxContextValue | null { - const contextValue = useContext(ReactReduxContext) - - if (process.env.NODE_ENV !== 'production' && !contextValue) { - throw new Error( - 'could not find react-redux context value; please ensure the component is wrapped in a ' - ) - } - - return contextValue -} +export const useReduxContext = /*#__PURE__*/ createReduxContextHook() diff --git a/src/hooks/useSelector.ts b/src/hooks/useSelector.ts index 626b0b483..7388044d2 100644 --- a/src/hooks/useSelector.ts +++ b/src/hooks/useSelector.ts @@ -1,6 +1,9 @@ -import { useContext, useDebugValue } from 'react' +import { useDebugValue } from 'react' -import { useReduxContext as useDefaultReduxContext } from './useReduxContext' +import { + createReduxContextHook, + useReduxContext as useDefaultReduxContext, +} from './useReduxContext' import { ReactReduxContext } from '../components/Context' import type { EqualityFn, NoInfer } from '../types' import type { uSESWS } from '../utils/useSyncExternalStore' @@ -28,7 +31,7 @@ export function createSelectorHook( const useReduxContext = context === ReactReduxContext ? useDefaultReduxContext - : () => useContext(context) + : createReduxContextHook(context) return function useSelector( selector: (state: TState) => Selected, diff --git a/src/hooks/useStore.ts b/src/hooks/useStore.ts index c1dca43cb..cf5d8868b 100644 --- a/src/hooks/useStore.ts +++ b/src/hooks/useStore.ts @@ -1,10 +1,13 @@ -import { useContext, Context } from 'react' +import { Context } from 'react' import { Action as BasicAction, AnyAction, Store } from 'redux' import { ReactReduxContext, ReactReduxContextValue, } from '../components/Context' -import { useReduxContext as useDefaultReduxContext } from './useReduxContext' +import { + createReduxContextHook, + useReduxContext as useDefaultReduxContext, +} from './useReduxContext' /** * Hook factory, which creates a `useStore` hook bound to a given context. @@ -21,7 +24,8 @@ export function createStoreHook< // @ts-ignore context === ReactReduxContext ? useDefaultReduxContext - : () => useContext(context) + : // @ts-ignore + createReduxContextHook(context) return function useStore< State = S, Action extends BasicAction = A diff --git a/test/hooks/useReduxContext.spec.tsx b/test/hooks/useReduxContext.spec.tsx index 18b31fb0d..0a2278aea 100644 --- a/test/hooks/useReduxContext.spec.tsx +++ b/test/hooks/useReduxContext.spec.tsx @@ -1,5 +1,10 @@ import { renderHook } from '@testing-library/react-hooks' -import { useReduxContext } from '../../src/hooks/useReduxContext' +import { createContext } from 'react' +import { ReactReduxContextValue } from '../../src/components/Context' +import { + createReduxContextHook, + useReduxContext, +} from '../../src/hooks/useReduxContext' describe('React', () => { describe('hooks', () => { @@ -13,6 +18,21 @@ describe('React', () => { /could not find react-redux context value/ ) + spy.mockRestore() + }) + }) + describe('createReduxContextHook', () => { + it('throws if component is not wrapped in provider', () => { + const customContext = createContext(null as any) + const useCustomReduxContext = createReduxContextHook(customContext) + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}) + + const { result } = renderHook(() => useCustomReduxContext()) + + expect(result.error.message).toMatch( + /could not find react-redux context value/ + ) + spy.mockRestore() }) })