Part 3: Building a React Product List App with Functional Cart using Context API #react #coding #javascript

Posted by


In Part 3 of our React Product List App with Functional Cart tutorial series, we will implement the Context API to manage the state of our Shopping Cart and provide a global state to our components. By using the Context API, we can avoid prop drilling and make our code cleaner and more maintainable.

Let’s start by creating a new file called CartContext.js in the src folder of our project. In this file, we will create a new context using React createContext() function and provide a default value for our Cart context.

import React, { createContext, useReducer } from "react";

export const CartContext = createContext();

const initialState = {
  cart: [],
};

const cartReducer = (state, action) => {
  switch (action.type) {
    case "ADD_TO_CART":
      return {
        ...state,
        cart: [...state.cart, action.payload],
      };
    case "REMOVE_FROM_CART":
      const updatedCart = state.cart.filter(
        (item) => item.id !== action.payload.id
      );
      return {
        ...state,
        cart: updatedCart,
      };
    case "CLEAR_CART":
      return {
        ...state,
        cart: [],
      };
    default:
      return state;
  }
};

export const CartProvider = ({ children }) => {
  const [state, dispatch] = useReducer(cartReducer, initialState);

  return (
    <CartContext.Provider value={{ state, dispatch }}>
      {children}
    </CartContext.Provider>
  );
};

In this code snippet, we are creating a CartContext using createContext() and providing a default value for our cart state using useReducer hook. We have defined three actions for our cartReducer: ADD_TO_CART, REMOVE_FROM_CART, and CLEAR_CART. These actions will be dispatched from our components to update the cart state.

Now, let’s update our App component to wrap our entire application with the CartProvider.

import React from "react";
import ProductList from "./ProductList";
import Cart from "./Cart";
import { CartProvider } from "./CartContext";

function App() {
  return (
    <CartProvider>
      <div className="App">
        <h1>React Product List App with Functional Cart</h1>
        <ProductList />
        <Cart />
      </div>
    </CartProvider>
  );
}

export default App;

By wrapping our App component with the CartProvider, we are providing the CartContext to all of our components. Now, we can access the cart state and dispatch function from any component that is a child of the CartProvider.

Let’s update our ProductList and Cart components to use the CartContext.

import React, { useContext } from "react";
import { CartContext } from "./CartContext";

function ProductList() {
  const { dispatch } = useContext(CartContext);

  const addToCart = (product) => {
    dispatch({ type: "ADD_TO_CART", payload: product });
  };

  return (
    <>
      <h2>Product List</h2>
      <ul>
        <li>
          <button onClick={() => addToCart({ id: 1, name: "Product 1" })}>
            Add Product 1 to Cart
          </button>
        </li>
        <li>
          <button onClick={() => addToCart({ id: 2, name: "Product 2" })}>
            Add Product 2 to Cart
          </button>
        </li>
      </ul>
    </>
  );
}

export default ProductList;

In this updated ProductList component, we are using the useContext hook to access the dispatch function from the CartContext. When a user clicks on the "Add to Cart" button, we are dispatching an action of type ADD_TO_CART with the product object as the payload.

import React, { useContext } from "react";
import { CartContext } from "./CartContext";

function Cart() {
  const { state, dispatch } = useContext(CartContext);

  const removeFromCart = (product) => {
    dispatch({ type: "REMOVE_FROM_CART", payload: product });
  };

  const clearCart = () => {
    dispatch({ type: "CLEAR_CART" });
  };

  return (
    <>
      <h2>Shopping Cart</h2>
      <ul>
        {state.cart.map((product) => (
          <li key={product.id}>
            {product.name}{" "}
            <button onClick={() => removeFromCart(product)}>Remove</button>
          </li>
        ))}
      </ul>
      <button onClick={clearCart}>Clear Cart</button>
    </>
  );
}

export default Cart;

In the updated Cart component, we are also using the useContext hook to access the cart state and dispatch function from the CartContext. We are rendering the items in the cart and providing a "Remove" button to remove items from the cart. We also added a "Clear Cart" button to clear all items from the cart.

With this implementation of the Context API, our React Product List App with Functional Cart now has a global state for the shopping cart that can be accessed from any component. We have successfully avoided prop drilling and made our code more maintainable and scalable.

I hope this tutorial was helpful in learning how to implement the Context API in a React application. Stay tuned for more tutorials on React and web development!