To start working with Read/Write Context, create a pair of Provider/Consumer components in the same way as for React Context API:
let CounterContext = createContext();Using <Provider /> you define the root of the state. Once parent is removed,
state disappears as well. This allows maintaining the life cycle of different
states.
function CounterView() {
return (
<CounterContext.Provider>
<CounterOutput />
<CounterHandlers />
</CounterContext.Provider>
);
}To render the state use <Consumer /> in the same way as you would expect.
function CounterOutput() {
return (
<CounterContext.Consumer>
{value => <p>{value}</p>}
</CounterContext.Consumer>
);
}In addition to the current state, <Consumer /> provides a method that can be
use for updating the state of the closest <Provider />.
function CounterHandlers() {
return (
<CounterContext.Consumer>
{(value, setState) => (
<button onClick={() => setState(value + 1)}>
Click to increment
</button>
)}
</CounterContext.Consumer>
);
}By default, setState() method of <Consumer /> swaps the state with anything
that it receives as an argument. If a certain logic needed for processing state
updates, createContext() can receive a reducer function.
Given the counter example above, imagine defining reducer function that allows consumers to trigger state changes via Flux-like "actions".
let CounterContext = createContext(
(state, input) => {
switch (input) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
}
}
);Since setState() callback is a positional argument, you're free to call it in
the way that suitable for the purpose:
function CounterHandlers() {
return (
<CounterContext.Consumer>
{(value, dispatch) => (
<React.Fragment>
<button onClick={() => dispatch('INCREMENT')}>
Increment
</button>
<button onClick={() => dispatch('DECREMENT')}>
Decrement
</button>
</React.Fragment>
)}
</CounterContext.Consumer>
);
}Unlicensed. Feel free to copy the code and readme.