React Redux is definetely an overkill for small and medium sized project and even for bigger project, it is often missused like a cache management rather than a state management system.
There are quite a few state management solution and we can also use Context API with useReducer
hook to create global or isolated states for our components. It a very easy and efficient way to have a state management without installing third party libraries and components, it’s built in the core of React.
We’ll create a counter component that will have its base and its value into 2 separate components. The base component Counter
will also have 4 buttons for adding a value, subtracting a value, increasing and decreasing by 1. There we’ll also have a reset button located into the root app component that it will reset the counter value to its initial state.
Create a CounterContext
and a Reducer
We create a context for the counter state witch it will be just a number variable
import React, { createContext, useReducer } from 'react';
// We create the context and the initial value constant and initialization function
export const CounterContext = createContext();
const initialCounter = 0;
const initCounter = () => initialCounter;
// We create a reducer with 4 methods
// add will add a value passed to the action, it works for negative number so we also have subtraction, 2 in 1 !
// inc will increase the state
// dec will decrease the state
// reset will reset the counter to the initial value
function counterReducer(state, action) {
switch (action.type) {
case 'add':
const { value } = action;
return state += value;
case 'inc': return ++state;
case 'dec': return --state;
case 'reset': return initCounter();
default: return state;
}
}
Of course we need to export the CounterContext
so to import and use it into the Counter
and Number
components.
Inside the App
component we use useReducer
to create a state with the initial value for the counter and the counterReducer
actions mapped to the dispatcher. We also wrap the Counter
component using the CounterContext.Provider
passing the values of the reducer we created counter
and dispatch
.
function App() {
const [counter, dispatch] = useReducer(counterReducer, initialCounter, initCounter);
return (
<div>
<CounterContext.Provider value={{ counter, dispatch }}>
<Counter />
</CounterContext.Provider>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}
We can now use reducer state and dispatcher to any child components and in this particular case, to our Counter
component. We also add a reset button outside the counter context provider because it will use the dispatch
reducer method that we already have.
Create the Number
component
The Number
component will just retrieve the counter value that we’ll have inside the CounterContext
and it will display it
import React, { useContext } from 'react';
import { CounterContext } from './index.js'
export default function Number() {
// Retrieve the counter state from the counter context
const { counter } = useContext(CounterContext);
// Render a span with the counter value
return <span style={{ color: 'teal' }}>{counter}</span>;
}
Create the Counter
component
The Counter
component will use the Number
component to display the counter value and it will have 4 buttons to perform addition, subtraction, increment and decrement to the counter value. We’ll use useContext
with the CounterContext
to get the reducer action dispatch
method so we use to perform our actions
import React, { useContext } from 'react';
import Number from './Number';
import { CounterContext } from './index.js'
export default function Counter() {
const { dispatch } = useContext(CounterContext);
return (
<div>
<h2>Counter <Number /></h2>
<div className="buttons">
<button onClick={() => dispatch({ type: 'add', value: 2 })}>Add 2</button>
<button onClick={() => dispatch({ type: 'add', value: -3 })}>Sub 3</button>
<button onClick={() => dispatch({ type: 'inc' })}>Inc</button>
<button onClick={() => dispatch({ type: 'dec' })}>Dec</button>
</div>
</div>
);
}
And we are ready. Counter
will use dispatch
from the CounterContext
to perform the reducer action that will alter the counter reducer state value that will display into the Number
component.