Skip to content

Commit

Permalink
Merge pull request #1990 from EskiMojo14/custom-context-invariant
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Jun 11, 2023
2 parents ee7ac84 + 52a16e3 commit b5a6d14
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 18 deletions.
33 changes: 22 additions & 11 deletions src/hooks/useReduxContext.ts
Expand Up @@ -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 `<Provider>`.
* @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 <Provider>'
)
}

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.
Expand All @@ -18,14 +39,4 @@ import type { ReactReduxContextValue } from '../components/Context'
* return <div>{store.getState()}</div>
* }
*/
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 <Provider>'
)
}

return contextValue
}
export const useReduxContext = /*#__PURE__*/ createReduxContextHook()
9 changes: 6 additions & 3 deletions 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'
Expand Down Expand Up @@ -28,7 +31,7 @@ export function createSelectorHook(
const useReduxContext =
context === ReactReduxContext
? useDefaultReduxContext
: () => useContext(context)
: createReduxContextHook(context)

return function useSelector<TState, Selected extends unknown>(
selector: (state: TState) => Selected,
Expand Down
10 changes: 7 additions & 3 deletions 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.
Expand All @@ -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
Expand Down
22 changes: 21 additions & 1 deletion 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', () => {
Expand All @@ -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<ReactReduxContextValue>(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()
})
})
Expand Down

0 comments on commit b5a6d14

Please sign in to comment.