Solving the Re-render Issue with
useCallback Hook
In the previous example, we faced a problem where a memoized component was re-rendering unnecessarily because its function prop (removePerson) was being recreated each time the state changed. Let’s look at how we can fix this using the useCallback hook in React.
What is the
useCallback Hook?
The useCallback hook helps us keep the same version of a function between re-renders unless its dependencies change. This is helpful when you have functions that don't need to be recreated on every render.
useCallback takes two arguments:
-
The function you want to "remember" (in our case,
removePerson). - A list of things that can change (dependencies) — if any of these change, the function will be recreated. Otherwise, it will stay the same.
Recap of the Problem
Before we used useCallback, every time the count changed, the removePerson function was recreated. This caused the List component to re-render unnecessarily, even when the people list didn’t change.
How to Fix It with
useCallback
We need to "remember" the removePerson function so it doesn't get recreated every time the state changes. Here's how we can do that:
Updated Code: Using
useCallback to Fix the Issue
App.jsx (After Using useCallback)
import { useState, useCallback } from 'react';
import { data } from '../../../../data';
import List from './List';
const App = () => {
const [people, setPeople] = useState(data);
const [count, setCount] = useState(0);
// Memorizing the function using useCallback
const removePerson = useCallback(
(id) => {
const newPeople = people.filter((person) => person.id !== id);
setPeople(newPeople);
},
[people] // Only recreate the function if the people list changes
);
return (
<section>
<button
className="btn"
onClick={() => setCount(count + 1)}
style={{ marginBottom: '1rem' }}
>
count {count}
</button>
<List people={people} removePerson={removePerson} />
</section>
);
};
export default App;
Key Changes:
-
Using
useCallback: We wrap theremovePersonfunction inuseCallback, so it only changes when thepeoplelist changes. -
Dependencies: We pass
[people]as the dependency. This means the function will only be recreated when thepeoplelist changes, not when other state (likecount) changes.
Why the Dependency Array is Important
When we use useCallback with an empty dependency array ([]), the function will be created only once, when the component first loads. However, this can cause problems. Since we are updating the people array (when we remove a person), if we don't include people in the dependency list, the function won't update with the new people array.
The Gotcha: What Happens If Dependencies Are Missing
If the dependency array is empty ([]), the function will always reference the initial people array, even after we remove a person. This can cause bugs, like when we try to remove someone from the list but nothing happens after the first removal.
Here’s the issue with an empty dependency array:
const removePerson = useCallback(
(id) => {
console.log(people, id);
const newPeople = people.filter((person) => person.id !== id);
setPeople(newPeople);
},
[] // No dependencies
);
In this case, the function will always use the original people array, which doesn’t update after we remove a person. This is why we see strange behavior.
Fixing It: Correctly Using
people as a Dependency
Now, if we pass [people] as the dependency, the function will be updated every time the people list changes, and everything will work as expected.
Here’s the working solution with useCallback:
const removePerson = useCallback(
(id) => {
const newPeople = people.filter((person) => person.id !== id);
setPeople(newPeople);
},
[people] // Now the function updates whenever the people list changes
);
Visual Comparison: Before vs After
useCallback
Before Using
useCallback
- Every time we click the count button, the
removePersonfunction is recreated, causing unnecessary re-renders.
After Using
useCallback
- Now, the
removePersonfunction is only recreated when thepeoplelist changes, and unnecessary re-renders are prevented.
Conclusion
By using the useCallback hook:
-
We keep the same function reference unless the
peoplearray changes. -
We reduce unnecessary re-renders, especially for components like
List, where thepeoplelist is passed as a prop. - This makes our app more efficient, especially when dealing with functions that don’t need to be recreated every time the state changes.
This solution improves performance and keeps our app running smoothly! 😊
Credits: John Smilga's course


