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.