How to Manage State by Using the React Context API

When I work on large-scale applications, I use Redux for state management. However, when it comes to building smaller applications and smaller personal projects, Redux can feel cumbersome. So, what’s the best light-weight solution for state management of these smaller applications? React hooks work really well for smaller applications, but sometimes you need to share state between components, and passing that state through components via props can get a little clunky. That’s where React Context comes to the rescue.

By utilizing the React Context API, you can avoid prop drilling. It also helps keep your components cleaner and simpler.

Let’s walk through what this would look like if we’re adding React Context to an application. First, I like to organize my context within a contexts folder within the src directory. Then I create a context file where all the global state will live. For this example, let’s call the file ItemContext.js:

import { createContext, useState } from "react";
import PropTypes from "prop-types";
const ItemContext = createContext();
const ItemProvider = ({ children }) => {
  const [items, setItems] = useState([]);
  const addItem = (item) => {
    const index = items[items.length - 1].id + 1;
    item.id = index;
    const updatedItems = [...items, item];
    setItems(updatedItems);
  };
  const editItem = (id, item) => {
    const index = items.findIndex((e) => e.id === id);
    const updatedItems = [...items];
    updatedItems[index] = item;
    setItems(updatedItems);
  };
  const removeItem = (id) => {
    const updatedItems = [...items];
    setItems(updatedItems.filter((item) => item.id !== id));
  };
  const context = {
    addItem,
    editItem,
    items,
    removeItem,
  };
  return <ItemContext.Provider value={context}>{children}</ItemContext.Provider>;
};
export { ItemContext, ItemProvider };
ItemProvider.propTypes = {
  children: PropTypes.object,
};

Next, within the src directory, if you don’t already have a hooks directory, create one where we’ll add a new hook file, useItemContext.js:

import { useContext } from "react";
import { ItemContext } from "../contexts/ItemContext";
const useItemContext = () => {
  return useContext(ItemContext);
};
export default useItemContext;

This file probably looks fairly small, but it will help us cut back on copying and pasting the same lines of code when we want to reference this global state. Now, when we want to access the global state or update the state via various components, we can do so by importing the context hook:

import useItemContext from "../hooks/useItemContext";
const ExampleComponent = () => {
  const { addItem, editItem, removeItem } = useItemContext();
  const handleAdd = (item) => {
    addItem(item);
  };
  const handleRemove = () => {
    removeItem(item.id);
  };
  const handleSave = (item) => {
    editItem(item.id, item);
  };
  return (
    {Add your presentation logic here}
  );
};
export default ExampleComponent;