The possibilities of useReducer hook and context API

Mary Maina
5 min readJan 19, 2023

Managing state of React applications is an important factor to consider while building React websites. useState hook is one of the most common ways to manage local state but as the size of the application increases it becomes necessary to think about how to manage the global state. There are a lot of libraries used for state management in React like Redux, Recoil, Zustand, Mobx, or xstate. Other than the libraries there is a native way to manage your state in React using useReducer hook and context API.

What is useReducer anyway?

useReducer is a hook used to handle complex state manipulations and updates when certain actions are dispatched. It is similar to the reduce method in JavaScript, which is used to iterate over an array and combine its elements into a single value. The Redux library can be thought of as just a big optimized useReducer.

Your state is your “store,” and you define “actions” that instruct a “reducer” on how to change that store.

useReducer analogy

useReducer can be thought of as a backend mental model. The structure of a backend includes a database where data is persisted and an API that is used to modify the database.

How useReducer is similar to a backend

The database that stores your data is called state.

Dispatch is comparable to the database modification API endpoints. You can call HTTP endpoints on APIs and describe the kind of requests you want to make. The payload, which is the same as the body of a POST request, might contain additional data.

Actions in useReducer are simple javascript objects with the payload and type fields.

The reducer houses the API’s logic. When the backend gets an API call (a dispatch call), it responds with instructions on how to update the database based on the endpoint and request content (the action)

A quick comparison of useState and useReducer


const [state, setState] = useState(initialValue)

// init is optional
const [state, dispatch] = useReducer(reducer,initialValue, init)

The hook returns an array with two elements in both situations. The first is the state, and the second is a function called setState for useState and dispatch for useReducer that allows you to modify the state.

useReducer has an additional argument which is a function that helps to ensure that state transition is valid and safe.(reducer)

UseReducer flow

Terminologies:

reducer: The reducer is a function that takes in two arguments and returns the next state. The first is the state and the second is the action. It specifies how the state gets updated.

const reducer = (state, action) => {
const {payload, type} = action
switch(type){
case "ADD_TO_CART":
const cartItems = [...state.cart, { cartQuantity: 1, ...payload }]
return {...state, state.cart: cartItems}
default:
return state
}
};

initialState: The value from which the initial value is calculated. It can be a value of any type

init: The initializer function specifies how state is calculated which is initializing the state lazily. It is optional.

state: The current state that is set to the initial state during the first render.

import { useReducer } from 'react';
const products = [{
date: Mon Aug 01 2022 05:37:10 GMT+0300 (East Africa Time),
delivery: false,
id: "377e08c4-e9c0-4e68-8cc3-a9e29e3fe01a",
image:"https://loremflickr.com/300/200/cats?lock=82867",
name:"Awesome Rubber Pizza",
price: "269.00",
},{...},{...}]
const initialState = {
cart: [],
products
};
const [state, dispatch] = useReducer(reducer, initialState);

Dispatch: The dispatch is a function returned by useReducer hook. It lets you update a state to a different value and triggers a re-render. It takes in an action as an argument.

const handleAddtoCart = () =>{
dispatch({ type: 'ADD_TO_CART', payload: product})}
}
<button onClick={handleAddtoCart}>
Add to cart
</button>

Let’s build a shopping Cart!!

import { useReducer } from 'react';
const products = [{
date: Mon Aug 01 2022 05:37:10 GMT+0300 (East Africa Time),
delivery: false,
id: "377e08c4-e9c0-4e68-8cc3-a9e29e3fe01a",
image:"https://loremflickr.com/300/200/cats?lock=82867",
name:"Awesome Rubber Pizza",
price: "269.00",
},{...},{...}]
// initial state of the database
const initialState = {
cart: [],
products
};

// API logic: how to update the database when the
// 'Add to cart' API endpoint is called
const reducer = (state, action) => {
const {payload, type} = action
switch(type){
case "ADD_TO_CART":
const cartItems = [...state.cart, { cartQuantity: 1, ...payload }]
return {...state, state.cart: cartItems}
default:
return state
}
};
// you can think of this as initializing and setting
// up a connection to the backend
const [state, dispatch] = useReducer(reducer, initialState);

function App() {
return (
<div>
{state.products.map(product =>
<div key={product.id} >
<img src={product.image} alt={product.name} />
<p >{product.name}</p>
<p >{product.price}</p>
<button onClick={() => dispatch({ type: 'ADD_TO_CART', payload: product})}>
Add to cart
</button>
</div>)}

</div>
);
}

export default App;

But before we complete the shopping cart. Let us talk about context API.

Context API

useContext is a hook used to save state globally and make it available across the entire application.

useContext is used together with useReducer to allow the component co-located state to be accessible across the entire application.

How to create the context:

We can create context using the createContext function from react. The function requires an initial state which is passed in as an argument.

import React, { createContext, useContext,} from "react";
const initialState = {
products :Products,
cart: []
}
type initialStateType = {
cart : ProductsProps[],
products : ProductsProps[]
}
// Context inital state passed to the createContext function
const contextInitialState = {
state: initialState,
dispatch: () => null,
}
const ShoppingCart = createContext<{
state: initialStateType;
dispatch: React.Dispatch<any>;
}>(contextInitialState)

Create the Provider

A provider allows all its children components to subscribe to the context changes. The provider accepts children as its argument. The ShoppingCart.Provider accepts one prop which is the value prop. Everything passed to the value prop will be accessible to all its children.

interface Props {
children: React.ReactNode
}
const CartProvider = ({children}:Props) =>{

const [state, dispatch] = useReducer(productReducer, initialState)
return <ShoppingCart.Provider value={{state, dispatch}}>
{children}
</ShoppingCart.Provider>

}
export default CartProvider

Create the useContext

Let us create a custom hook called useShoppingCart. Inside this hook, we will use the useContext hook that allows us to access the state and dispatch function from any component in the hierarchy tree.

export const useShoppingCart = () =>{
return useContext(ShoppingCart)
}

Add the Provider to the Main file

Here we import the CartProvider and wrap our entire application with the CartProvide. This will allow us to share state and dispatch function across the entire app.

// main.tsx file
import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App'
import Navbar from './components/Navbar'
import CartProvider from './context/appContext'

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(

<BrowserRouter>
<CartProvider>
<Navbar/>
<App />
</CartProvider>

</BrowserRouter>



)

Cheers!! We can now access the state and dispatch functions in the Products component and cart component.

GitHub link: https://github.com/MainaMary/useReducer-demo

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Mary Maina
Mary Maina

Written by Mary Maina

Software Developer. Passion is the fire that Lights your way.

No responses yet

Write a response