React Context and Hook
15th April, 2023
Although I use this all the time I always have to look up how to implement it. This example is based on Kent C. Dodds’ article1 on the subject.
I’ve used this most recently when creating the Storypoint Shuffle2 app.
import * as React from 'react'
type Action = {type: 'increment'} | {type: 'decrement'}type Dispatch = (action: Action) => voidtype State = {count: number}type CountProviderProps = {children: React.ReactNode}
const CountStateContext = React.createContext< {state: State; dispatch: Dispatch} | undefined>(undefined)
function countReducer(state: State, action: Action) { switch (action.type) { case 'increment': { return {count: state.count + 1} } default: { throw new Error(`Unhandled action type: ${action.type}`) } }}
function CountProvider({children}: CountProviderProps) { const [state, dispatch] = React.useReducer(countReducer, {count: 0}) // NOTE: you *might* need to memoize this value // Learn more in http://kcd.im/optimize-context const value = {state, dispatch} return ( <CountStateContext.Provider value={value}> {children} </CountStateContext.Provider> )}
function useCount() { const context = React.useContext(CountStateContext) if (context === undefined) { throw new Error('useCount must be used within a CountProvider') } return context}
export {CountProvider, useCount}
The provider can then be used to wrap the rest of your app:
<CountProvider> <ChildComponents /></CountProvider>
And in your components that need the context:
const {state, dispatch} = useCount();