Skip to content

Commit d14cdcb

Browse files
author
ben.durrant
committed
check for Provider even when using custom context
1 parent 8d03182 commit d14cdcb

File tree

4 files changed

+55
-18
lines changed

4 files changed

+55
-18
lines changed

Diff for: src/hooks/useReduxContext.ts

+22-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@ import { useContext } from 'react'
22
import { ReactReduxContext } from '../components/Context'
33
import type { ReactReduxContextValue } from '../components/Context'
44

5+
/**
6+
* Hook factory, which creates a `useReduxContext` hook bound to a given context. This is a low-level
7+
* hook that you should usually not need to call directly.
8+
*
9+
* @param {React.Context} [context=ReactReduxContext] Context passed to your `<Provider>`.
10+
* @returns {Function} A `useReduxContext` hook bound to the specified context.
11+
*/
12+
export function createReduxContextHook(context = ReactReduxContext) {
13+
return function useReduxContext(): ReactReduxContextValue | null {
14+
const contextValue = useContext(context)
15+
16+
if (process.env.NODE_ENV !== 'production' && !contextValue) {
17+
throw new Error(
18+
'could not find react-redux context value; please ensure the component is wrapped in a <Provider>'
19+
)
20+
}
21+
22+
return contextValue
23+
}
24+
}
25+
526
/**
627
* A hook to access the value of the `ReactReduxContext`. This is a low-level
728
* hook that you should usually not need to call directly.
@@ -18,14 +39,4 @@ import type { ReactReduxContextValue } from '../components/Context'
1839
* return <div>{store.getState()}</div>
1940
* }
2041
*/
21-
export function useReduxContext(): ReactReduxContextValue | null {
22-
const contextValue = useContext(ReactReduxContext)
23-
24-
if (process.env.NODE_ENV !== 'production' && !contextValue) {
25-
throw new Error(
26-
'could not find react-redux context value; please ensure the component is wrapped in a <Provider>'
27-
)
28-
}
29-
30-
return contextValue
31-
}
42+
export const useReduxContext = /*#__PURE__*/ createReduxContextHook()

Diff for: src/hooks/useSelector.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { useContext, useDebugValue } from 'react'
1+
import { useDebugValue } from 'react'
22

3-
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
3+
import {
4+
createReduxContextHook,
5+
useReduxContext as useDefaultReduxContext,
6+
} from './useReduxContext'
47
import { ReactReduxContext } from '../components/Context'
58
import type { EqualityFn, NoInfer } from '../types'
69
import type { uSESWS } from '../utils/useSyncExternalStore'
@@ -28,7 +31,7 @@ export function createSelectorHook(
2831
const useReduxContext =
2932
context === ReactReduxContext
3033
? useDefaultReduxContext
31-
: () => useContext(context)
34+
: createReduxContextHook(context)
3235

3336
return function useSelector<TState, Selected extends unknown>(
3437
selector: (state: TState) => Selected,

Diff for: src/hooks/useStore.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
import { useContext, Context } from 'react'
1+
import { Context } from 'react'
22
import { Action as BasicAction, AnyAction, Store } from 'redux'
33
import {
44
ReactReduxContext,
55
ReactReduxContextValue,
66
} from '../components/Context'
7-
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
7+
import {
8+
createReduxContextHook,
9+
useReduxContext as useDefaultReduxContext,
10+
} from './useReduxContext'
811

912
/**
1013
* Hook factory, which creates a `useStore` hook bound to a given context.
@@ -21,7 +24,7 @@ export function createStoreHook<
2124
// @ts-ignore
2225
context === ReactReduxContext
2326
? useDefaultReduxContext
24-
: () => useContext(context)
27+
: createReduxContextHook(context)
2528
return function useStore<
2629
State = S,
2730
Action extends BasicAction = A

Diff for: test/hooks/useReduxContext.spec.tsx

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { renderHook } from '@testing-library/react-hooks'
2-
import { useReduxContext } from '../../src/hooks/useReduxContext'
2+
import { createContext } from 'react'
3+
import { ReactReduxContextValue } from '../../src/components/Context'
4+
import {
5+
createReduxContextHook,
6+
useReduxContext,
7+
} from '../../src/hooks/useReduxContext'
38

49
describe('React', () => {
510
describe('hooks', () => {
@@ -13,6 +18,21 @@ describe('React', () => {
1318
/could not find react-redux context value/
1419
)
1520

21+
spy.mockRestore()
22+
})
23+
})
24+
describe('createReduxContextHook', () => {
25+
it('throws if component is not wrapped in provider', () => {
26+
const customContext = createContext<ReactReduxContextValue>(null as any)
27+
const useCustomReduxContext = createReduxContextHook(customContext)
28+
const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
29+
30+
const { result } = renderHook(() => useCustomReduxContext())
31+
32+
expect(result.error.message).toMatch(
33+
/could not find react-redux context value/
34+
)
35+
1636
spy.mockRestore()
1737
})
1838
})

0 commit comments

Comments
 (0)