Zyzle.dev

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' article on the subject.

import * as React from 'react'
type Action = {type: 'increment'} | {type: 'decrement'}
type Dispatch = (action: Action) => void
type 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 like so:

<CountProvider>
<ChildComponents />
</CountProvider>

And in our components that need the context:

const {state, dispatch} = useCount();