How to Use useCallback for Performance in React
Use React's
useCallback hook to memoize functions so they keep the same reference between renders, preventing unnecessary re-renders of child components that depend on those functions. Wrap your function with useCallback and provide dependencies to update it only when needed.Syntax
The useCallback hook takes two arguments: the function you want to memoize and an array of dependencies. It returns a memoized version of the function that only changes if one of the dependencies changes.
- callback: The function to memoize.
- dependencies: Array of values that, when changed, update the memoized function.
javascript
const memoizedFunction = useCallback(() => { // function logic }, [dependencies]);
Example
This example shows a parent component passing a memoized callback to a child component. The child only re-renders when the callback changes, improving performance.
javascript
import React, { useState, useCallback } from 'react'; const Child = React.memo(({ onClick }) => { console.log('Child rendered'); return <button onClick={onClick}>Click me</button>; }); export default function Parent() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { alert('Button clicked!'); }, []); // No dependencies, function stays the same return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <Child onClick={handleClick} /> </div> ); }
Output
Count: 0
[Increment button]
[Click me button]
Console logs 'Child rendered' only once unless handleClick changes.
Common Pitfalls
Common mistakes include:
- Not providing dependencies or providing wrong dependencies, causing stale or recreated functions.
- Using
useCallbackunnecessarily for functions that don't cause re-renders. - Expecting
useCallbackto improve performance in all cases; sometimes it adds overhead.
javascript
import React, { useState, useCallback } from 'react'; // Wrong: missing dependencies causes stale closure const ParentWrong = () => { const [count, setCount] = useState(0); const handleClick = useCallback(() => { alert(`Count is ${count}`); // count is stale if not in deps }, []); // count missing here return <button onClick={handleClick}>Click</button>; }; // Right: include count in dependencies const ParentRight = () => { const [count, setCount] = useState(0); const handleClick = useCallback(() => { alert(`Count is ${count}`); }, [count]); return <button onClick={handleClick}>Click</button>; };
Quick Reference
- Use
useCallbackto memoize functions passed to child components. - Always include all variables used inside the callback in the dependency array.
- Don't overuse
useCallback; use it only when it prevents expensive re-renders. - Combine with
React.memoin child components for best effect.
Key Takeaways
Use
useCallback to keep function references stable between renders.Always list all dependencies in the dependency array to avoid stale data.
Combine
useCallback with React.memo to optimize child component rendering.Avoid unnecessary use of
useCallback to prevent added complexity.Test performance impact; sometimes
useCallback overhead outweighs benefits.