Give state and dispatch produced by useReducer() to context created by createContext() so that you can access the values smoothly in any components placed under <Context.Provider>.
The value which your context holds.
type CounterState = { count: number }The reducer actions which directs how to modifies XxxState value.
type CounterAction =
| { action: 'increment'; data: { amount: number } }
| { action: 'reset' }Optional but useful.
const initialCounterState: CounterState = { count: 0 };Function modifies XxxState value following XxxAction direction.
function reduceCounter(state: CounterState, action: CounterAction): CounterState {
if (action.type === 'increment') {
return { count: state.count + action.data.amount };
}
if (action.type === 'reset') {
return { count: 0 };
}
return state;
}Use React.createContext().
const CounterContext = createContext({
dispatch: (() => undefined) as React.Dispatch<CounterAction>,
state: { ...initialCounterState },
});These dispatch and state are never used as long as you use the context under provider. If you thought this should be optional, see
DefinitelyTyped/DefinitelyTyped#24509 (comment)
Note: don't use another one; vm.createContext() is wrong.
The top level component provides context with values.
- Wrap all with the context provider:
<XxxContext.Provider> - Prepare a pair of state and dispatcher by
React.useReducer() - Give them to the provider
- Add any child components under the provider
const CounterPage: React.FC = () => {
const [state, dispatch] = useReducer(reduceCounter, { ...initialCounterState });
return (
<CounterContext.Provider value={{ state, dispatch }}>
<div className="CounterPage">
<h1>Counter</h1>
{/* add child components here */}
</div>
</CounterContext.Provider>
);
};- Retrieve context by
React.useContext() - Use values and/or invoke dispatcher
To use context state value:
const ChildCounter: React.FC = () => {
const { state: { count } } = useContext(CounterContext);
return (
<div className="ChildCounter">
Count: {count}
</div>
);
};To invoke reducer function:
const CounterForm: React.FC = () => {
const { dispatch } = useContext(CounterContext);
const onIncrementClick = useCallback(() => {
dispatch({ type: 'increment', data: { amount: 1 } });
}, []);
const onResetClick = useCallback(() => {
dispatch({ type: 'reset' });
}, []);
return (
<div className="CounterForm">
<button onClick={onIncrementClick}>+1</button>
<button onClick={onResetClick}>Reset</button>
</div>
);
};Make sure these components are placed under <XxxContext.Provider> otherwise the context values would be the initial ones always.