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;
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);
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;
}
Output:
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 📋
useCallback
is 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)