Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 83

Real-Time Cart Item Count Display

Recommended resources

Hello! Welcome to the final lesson on building our shopping cart.

In our last session, we implemented the "remove from cart" feature, completing the core state management logic for our cart. We now have a robust system using useReducer and Context that can add, update, and remove items. However, that state is currently only visible on the cart page itself.

Today, we will connect our cart's state to the rest of the application by displaying a real-time cart item count indicator in the application header. This is a small but crucial feature for user experience, providing immediate feedback and making the application feel more cohesive. It's also a perfect demonstration of the power of the React Context API for sharing state across distant components.

1. Accessing Global State with useContext

Our CartProvider wraps the entire application, which means any component in the tree can "subscribe" to the cart's state. To do this from our Navbar component, we'll use the useContext hook.

First, open your Navbar.jsx component file (e.g., src/components/Navbar.jsx). We need to import useContext from React and our CartContext. Then, we can call useContext to get access to the cart's state and dispatch function. For this task, we only need the state.

// src/components/Navbar.jsx
import React, { useContext } from 'react';
import { Link, NavLink } from 'react-router-dom';
import { CartContext } from '../context/CartContext'; // Adjust path if needed

const Navbar = () => {
  const { state } = useContext(CartContext);

  // We will calculate the item count here...

  return (
    <header className="bg-white shadow-md">
      <nav className="container mx-auto px-6 py-3 flex justify-between items-center">
        <Link to="/" className="text-xl font-bold text-gray-800">
          MyStore
        </Link>
        <div className="flex items-center">
          <NavLink to="/" className="text-gray-600 hover:text-blue-500 mx-4" end>
            Store
          </NavLink>
          {/* Cart Link will go here */}
        </div>
      </nav>
    </header>
  );
};

export default Navbar;

With const { state } = useContext(CartContext);, our Navbar is now connected to the cart. Whenever the cart state changes (e.g., an item is added), this component will automatically re-render with the latest data.


2. Calculating the Total Item Count with .reduce()

You might be tempted to use state.items.length to get the item count. However, this would only count the number of unique products in the cart, not the total quantity. If a user adds 5 of the same item, state.items.length would still be 1.

This is a perfect opportunity to use one of the array methods you wanted to master: Array.prototype.reduce(). We can use it to iterate over the items array and sum up all the quantity values.

Let's add this calculation to our Navbar component.

// Inside your Navbar component, after the useContext hook
const totalItemCount = state.items.reduce((sum, currentItem) => {
  return sum + currentItem.quantity;
}, 0); // The '0' is the initial value for 'sum'

This reduce function:

  1. Starts with an initial sum of 0.
  2. For each currentItem in the state.items array, it adds currentItem.quantity to the sum.
  3. After iterating through all items, it returns the final sum.

This gives us the true total number of items in the cart.


3. Displaying the Indicator with Conditional Rendering

Now that we have totalItemCount, we can display it in the UI. We only want the indicator (the "badge") to show up if there are items in the cart. We can achieve this easily with the logical && operator for conditional rendering.

Let's add the link to our cart page and include a <span> that displays the count only when totalItemCount > 0.

Here is the complete Navbar.jsx with all the pieces integrated.

// src/components/Navbar.jsx
import React, { useContext } from 'react';
import { Link, NavLink } from 'react-router-dom';
import { CartContext } from '../context/CartContext';

const Navbar = () => {
  // 1. Access the global cart state
  const { state } = useContext(CartContext);

  // 2. Calculate the total quantity of items
  const totalItemCount = state.items.reduce((sum, item) => sum + item.quantity, 0);

  return (
    <header className="bg-white shadow-md sticky top-0 z-10">
      <nav className="container mx-auto px-6 py-4 flex justify-between items-center">
        <Link to="/" className="text-2xl font-bold text-gray-800">
          MyStore
        </Link>
        <div className="flex items-center">
          <NavLink
            to="/"
            className="text-gray-600 hover:text-blue-500 mx-4"
            end // 'end' prop ensures this link is only active for the exact home path
          >
            Store
          </NavLink>

          {/* 3. Render the cart link and conditional badge */}
          <NavLink to="/cart" className="relative text-gray-600 hover:text-blue-500">
            <svg className="h-6 w-6" fill="none" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" viewBox="0 0 24 24" stroke="currentColor">
              <path d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
            </svg>
            {totalItemCount > 0 && (
              <span className="absolute -top-2 -right-2 h-5 w-5 rounded-full bg-red-500 text-white text-xs flex items-center justify-center">
                {totalItemCount}
              </span>
            )}
          </NavLink>
        </div>
      </nav>
    </header>
  );
};

export default Navbar;

I've added a simple SVG for a cart icon and used some Tailwind CSS classes for positioning and styling the badge. Now, when you add items to the cart from anywhere in your app, the count in the header will update instantly!

To see a similar implementation in action, you can refer to the following resource.

Learn how React Context API works by Building a Minimal ...

The article 'Learn how React Context API works by Building a Minimal ...' by Israel Mitolu provides a complete walkthrough of an e-commerce app using Context. The section on the Navbar component shows the same pattern we just implemented.

In the resource, find the Navbar.jsx code block. Notice how it uses useContext(CartContext) to get cartItems and then uses conditional rendering ({cartItems.length > 0 && ...}) to display a count. Our implementation is slightly more robust by using .reduce() to sum quantities, but the core principle of consuming context in the header is identical.


An Optimization Note

In our Navbar, we calculate totalItemCount on every render. For this app, it's perfectly fine. However, in very large applications, you might want to avoid recalculating derived values like this in multiple components.

A common optimization is to calculate this value once inside the reducer and store it on the state object. For example, your reducer could return { ...state, items: newItems, itemCount: newItems.reduce(...) } after every action. This way, components just read the pre-calculated value from the context, which is slightly more efficient. This is something to keep in mind as you build more complex applications.


Conclusion

You have successfully connected your global cart state to your main application header, providing a seamless user experience.

Key Takeaways:

  • The useContext hook allows any component to subscribe to a context's value.
  • React's automatic re-renders ensure the UI stays in sync with the state.
  • Array.prototype.reduce() is a powerful tool for aggregating data from an array, such as summing up item quantities.
  • Conditional rendering with && is a clean way to show or hide UI elements based on state.

This lesson concludes our work on the shopping cart's front-end logic. You've built a robust, self-contained state management system for a key e-commerce feature.

Preview of the Next Module:

So far, our React app has been a standalone client. In the next module, we'll begin the exciting process of connecting it to your Laravel backend. The first, and most critical, step is to configure your Laravel backend to handle Cross-Origin Resource Sharing (CORS). This will allow your React app, running on one server, to make API requests to your Laravel app, running on another.