React Hooks with Context + Reducer

Using Context and Reducers with React Hooks

July 24, 2020
In category Development

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.

0 0

comments powered by Disqus