You do not need to put them in separate ContextProviders if almost all of your components are using both state and dispatch, just make sure that you are memoizing the argument you pass to the provider value when you pass them as a object. This way your consumers will only re-render when state actually changes. Also note that dispatch
instance doesn't actually change so there is no point in creating aa separate context for it
export const CounterProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, 0)
const contextValue = useMemo(() => ({state, dispatch}), [state, dispatch])
return (
<CounterContext.Provider value={contextValue}>
{children}
</CounterContext.Provider>
)
}
EDIT:
As @dciccale pointed out in the comments, it makes a lot of sense to keep dispatch and state in separate contexts if there are quite a lot of components in your app which largely only use dispatch and hence they will not re-render if state changes.
Even if we memorize contextValue
with useMemo hook, contextValue
will still be re-evaluated every time we update state
's value, causing all components depending on the context to rerender, even some of them only depend on dispatch
only.
See also: https://hswolff.com/blog/how-to-usecontext-with-usereducer/#performance-concerns
Evaluate your app requirements and take a decision wisely.