Skip to content

Commit

Permalink
feat: Hook creators accept context directly
Browse files Browse the repository at this point in the history
  • Loading branch information
ryaninvents committed Jun 12, 2019
1 parent efd270f commit 69006eb
Show file tree
Hide file tree
Showing 8 changed files with 33 additions and 50 deletions.
8 changes: 3 additions & 5 deletions docs/api/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,19 +306,17 @@ To access an alternate context via the hooks API, use the hook creator functions
import React from 'react'
import {
Provider,
createReduxContextHook,
createStoreHook,
createDispatchHook,
createSelectorHook
} from 'react-redux'

const MyContext = React.createContext(null)
const useMyReduxContext = createReduxContextHook(MyContext)

// Export your custom hooks if you wish to use them in other files.
export const useStore = createStoreHook(useMyReduxContext)
export const useDispatch = createDispatchHook(useMyReduxContext)
export const useSelector = createSelectorHook(useMyReduxContext)
export const useStore = createStoreHook(MyContext)
export const useDispatch = createDispatchHook(MyContext)
export const useSelector = createSelectorHook(MyContext)

const myStore = createStore(rootReducer)

Expand Down
10 changes: 5 additions & 5 deletions src/hooks/useDispatch.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { ReactReduxContext } from '../components/Context'
import { useStore as useDefaultStore, createStoreHook } from './useStore'

/**
* Hook factory, which creates a `useDispatch` hook bound to a given context.
*
* @param {Function} [useReduxContext] Hook which returns the Redux context.
* @param {Function} [context=ReactReduxContext] Context passed to your `<Provider>`.
* @returns {Function} A `useDispatch` hook bound to the specified context.
*/
export function createDispatchHook(useReduxContext = null) {
const useStore = useReduxContext
? createStoreHook(useReduxContext)
: useDefaultStore
export function createDispatchHook(context = ReactReduxContext) {
const useStore =
context === ReactReduxContext ? useDefaultStore : createStoreHook(context)
return function useDispatch() {
const store = useStore()
return store.dispatch
Expand Down
31 changes: 11 additions & 20 deletions src/hooks/useReduxContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,11 @@ import { useContext } from 'react'
import invariant from 'invariant'
import { ReactReduxContext } from '../components/Context'

/**
* Hook factory, which creates a `useReduxContext` hook bound to a given context.
*
* @param {Function} [context=ReactReduxContext] React context passed to the `context` prop of your `<Provider>`.
* @returns {Function} A `useReactContext` hook bound to the specified context.
*/
export function createReduxContextHook(context = ReactReduxContext) {
return function useReduxContext() {
const contextValue = useContext(context)

invariant(
contextValue,
'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.
*
* @param {Function} [context=ReactReduxContext] Context passed to your `<Provider>`, if you're not using the default.
* @returns {any} the value of the `ReactReduxContext`
*
* @example
Expand All @@ -37,4 +19,13 @@ export function createReduxContextHook(context = ReactReduxContext) {
* return <div>{store.getState()}</div>
* }
*/
export const useReduxContext = createReduxContextHook()
export function useReduxContext(context = ReactReduxContext) {
const contextValue = useContext(context)

invariant(
contextValue,
'could not find react-redux context value; please ensure the component is wrapped in a <Provider>'
)

return contextValue
}
9 changes: 5 additions & 4 deletions src/hooks/useSelector.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useReducer, useRef, useEffect, useMemo, useLayoutEffect } from 'react'
import invariant from 'invariant'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
import { useReduxContext } from './useReduxContext'
import Subscription from '../utils/Subscription'
import { ReactReduxContext } from '../components/Context'

// React currently throws a warning when using useLayoutEffect on the server.
// To get around it, we can conditionally useEffect on the server (no-op) and
Expand Down Expand Up @@ -95,14 +96,14 @@ function useSelectorWithStoreAndSubscription(
/**
* Hook factory, which creates a `useSelector` hook bound to a given context.
*
* @param {Function} [useReduxContext] Hook which returns the Redux context.
* @param {Function} [context=ReactReduxContext] Context passed to your `<Provider>`.
* @returns {Function} A `useSelector` hook bound to the specified context.
*/
export function createSelectorHook(useReduxContext = useDefaultReduxContext) {
export function createSelectorHook(context = ReactReduxContext) {
return function useSelector(selector, equalityFn = refEquality) {
invariant(selector, `You must pass a selector to useSelectors`)

const { store, subscription: contextSub } = useReduxContext()
const { store, subscription: contextSub } = useReduxContext(context)

return useSelectorWithStoreAndSubscription(
selector,
Expand Down
9 changes: 5 additions & 4 deletions src/hooks/useStore.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
import { ReactReduxContext } from '../components/Context'
import { useReduxContext } from './useReduxContext'

/**
* Hook factory, which creates a `useStore` hook bound to a given context.
*
* @param {Function} [useReduxContext] Hook which returns the Redux context.
* @param {Function} [context=ReactReduxContext] Context passed to your `<Provider>`.
* @returns {Function} A `useStore` hook bound to the specified context.
*/
export function createStoreHook(useReduxContext = useDefaultReduxContext) {
export function createStoreHook(context = ReactReduxContext) {
return function useStore() {
const { store } = useReduxContext()
const { store } = useReduxContext(context)
return store
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import connect from './connect/connect'
import { useDispatch, createDispatchHook } from './hooks/useDispatch'
import { useSelector, createSelectorHook } from './hooks/useSelector'
import { useStore, createStoreHook } from './hooks/useStore'
import { createReduxContextHook } from './hooks/useReduxContext'

import { setBatch } from './utils/batch'
import { unstable_batchedUpdates as batch } from './utils/reactBatchedUpdates'
Expand All @@ -26,6 +25,5 @@ export {
createSelectorHook,
useStore,
createStoreHook,
createReduxContextHook,
shallowEqual
}
7 changes: 2 additions & 5 deletions test/hooks/useDispatch.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import { renderHook } from 'react-hooks-testing-library'
import {
Provider as ProviderMock,
useDispatch,
createDispatchHook,
createReduxContextHook
createDispatchHook
} from '../../src/index.js'

const store = createStore(c => c + 1)
Expand All @@ -25,9 +24,7 @@ describe('React', () => {
describe('createDispatchHook', () => {
it("returns the correct store's dispatch function", () => {
const nestedContext = React.createContext(null)
const useCustomDispatch = createDispatchHook(
createReduxContextHook(nestedContext)
)
const useCustomDispatch = createDispatchHook(nestedContext)
const { result } = renderHook(() => useDispatch(), {
// eslint-disable-next-line react/prop-types
wrapper: ({ children, ...props }) => (
Expand Down
7 changes: 2 additions & 5 deletions test/hooks/useSelector.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import {
useSelector,
shallowEqual,
connect,
createSelectorHook,
createReduxContextHook
createSelectorHook
} from '../../src/index.js'
import { useReduxContext } from '../../src/hooks/useReduxContext'

Expand Down Expand Up @@ -403,9 +402,7 @@ describe('React', () => {

it('subscribes to the correct store', () => {
const nestedContext = React.createContext(null)
const useCustomSelector = createSelectorHook(
createReduxContextHook(nestedContext)
)
const useCustomSelector = createSelectorHook(nestedContext)
let defaultCount = null
let customCount = null

Expand Down

0 comments on commit 69006eb

Please sign in to comment.