DEV Community

Cover image for Mastering State Management with the useCallback( ) Hook in React.
Sudhanshu Gaikwad
Sudhanshu Gaikwad

Posted on

Mastering State Management with the useCallback( ) Hook in React.

The useCallback() Hook in React is a performance booster that helps keep your app fast by memoizing functions. Memoization is like "freezing" a function so it doesn’t change unless certain conditions do. This means React can skip recreating it on every render, which is especially helpful in complex apps.

What Exactly is useCallback? 🤔

The useCallback() Hook returns a memoized version of a function. This means the function only updates if its dependencies (variables it relies on) change. This is super useful in preventing unnecessary re-renders.

Why Should You Care About useCallback? 🛠️

When you pass a function as a prop to a child component, React recreates it every time the parent component renders. Without useCallback, this can cause child components to re-render unnecessarily, slowing down your app. By using useCallback, we control when the function is recreated, which speeds things up!

Example: A Shopping Cart with useCallback

Let’s dive into an example to make this clear. Imagine we have a shopping cart app where the parent component has a function to add items to the cart. We’ll use useCallback to prevent the cart component from re-rendering unless there’s an actual change in the items.

App.js

// App.js
import React, { useState, useCallback } from "react";
import Cart from "./Cart";

const App = () => {
  const [count, setCount] = useState(0);
  const [cartItems, setCartItems] = useState([]);

  // Increment function for demo purposes
  const increment = () => setCount((c) => c + 1);

  // Memoized addItem function to prevent unnecessary re-renders
  const addItem = useCallback(() => {
    setCartItems((items) => [...items, `Item ${items.length + 1}`]);
  }, [cartItems]);

  return (
    <div style={{ padding: "20px" }}>
      <h1>Shopping Cart Demo</h1>
      <Cart items={cartItems} addItem={addItem} />
      <hr />
      <div>
        Count: {count}
        <button onClick={increment}>Increment Count</button>
      </div>
    </div>
  );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

Card.jsx

// Cart.js
import React, { memo } from "react";

const Cart = ({ items, addItem }) => {
  console.log("Cart component re-rendered");

  return (
    <div>
      <h2>Items in Cart</h2>
      {items.map((item, index) => (
        <p key={index}>{item}</p>
      ))}
      <button onClick={addItem}>Add Item</button>
    </div>
  );
};

export default memo(Cart);

Enter fullscreen mode Exit fullscreen mode

index.css

/* index.css */

/* Reset and base styles */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Arial", sans-serif;
}

body {
  background-color: #f4f4f9;
  color: #333;
}

/* Container styling */
.app-container {
  max-width: 600px;
  margin: 50px auto;
  padding: 20px;
  background-color: #ffffff;
  border-radius: 10px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
  text-align: center;
}

/* Responsive styling */
@media (max-width: 600px) {
  .app-container {
    max-width: 90%;
    padding: 15px;
  }
}

h1 {
  font-size: 2.2rem;
  color: #2c3e50;
  margin-bottom: 20px;
}

/* Button styling */
button {
  margin-top: 10px;
  padding: 10px 20px;
  font-size: 1rem;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.3s, transform 0.2s;
}

button:hover {
  background-color: #2980b9;
  color: #fff;
  transform: scale(1.05);
}

button:active {
  transform: scale(0.95);
}

/* Increment button specific styling */
.increment-button {
  background-color: #3498db;
  color: white;
}

/* Cart container responsive adjustments */
.cart-container {
  width: 100%;
}

.cart-item {
  padding: 8px;
  margin: 5px 0;
  background-color: #ecf0f1;
  border-radius: 5px;
}

Enter fullscreen mode Exit fullscreen mode

Output:

Image description

How This Works 🎉

  • addItem function is memoized: We used useCallback to memoize addItem. This ensures React doesn’t recreate it unless cartItems changes.
  • Cart component is wrapped in React.memo: This prevents re-renders unless the items or addItem props actually change.
  • Efficient renders: Now, clicking on the "Increment Count" button only affects the count state and doesn’t re-render Cart, saving processing time.

Quick Recap 📋

useCallbackis for memoizing functions to prevent re-renders when dependencies haven't changed.
Great for functions that don’t need to update on every render.
Combine useCallback with React.memo to optimize component re-renders.

Top comments (0)