How to Use useCallback in React: Simple Guide with Examples
In React,
useCallback is a hook that returns a memoized version of a callback function, which only changes if its dependencies change. Use it to prevent unnecessary re-creation of functions on every render, improving performance especially when passing callbacks to child components.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 updates when one of the dependencies changes.
callback: The function you want to keep stable between renders.dependencies: An array of values that, when changed, will update the memoized function.
javascript
const memoizedCallback = useCallback(() => { // Your function code here }, [dependency1, dependency2]);
Example
This example shows a parent component that uses useCallback to memoize a function passed to a child component. The child only re-renders when the memoized function changes, avoiding unnecessary renders.
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
When you click 'Increment', only the parent re-renders and 'Child rendered' does NOT log again because the memoized function did not change. Clicking 'Click me' shows an alert.
Common Pitfalls
Common mistakes when using useCallback include:
- Not listing all dependencies in the dependency array, causing stale closures.
- Using
useCallbackunnecessarily for functions that don't cause re-renders or performance issues. - Expecting
useCallbackto fix all performance problems; it only helps with function identity.
javascript
import React, { useState, useCallback } from 'react'; function Example() { const [count, setCount] = useState(0); // Wrong: missing 'count' in dependencies causes stale value const incrementWrong = useCallback(() => { setCount(count + 1); // 'count' is stale here }, []); // Right: include 'count' in dependencies const incrementRight = useCallback(() => { setCount(count + 1); }, [count]); return ( <div> <button onClick={incrementWrong}>Wrong Increment</button> <button onClick={incrementRight}>Right Increment</button> <p>Count: {count}</p> </div> ); }
Output
Clicking 'Wrong Increment' will not update count correctly because it uses a stale count value. 'Right Increment' updates count properly.
Quick Reference
- Purpose: Memoize functions to avoid re-creating them on every render.
- Use when: Passing callbacks to optimized child components or expensive computations inside callbacks.
- Dependencies: Always include all variables used inside the callback.
- Don't use: For every function; only when it improves performance.
Key Takeaways
Use
useCallback to memoize functions and prevent unnecessary re-renders.Always include all dependencies in the dependency array to avoid stale data.
Avoid overusing
useCallback; use it only when it improves performance.Memoized functions keep the same identity unless dependencies change.
Combine
useCallback with React.memo for best child component optimization.