0
0
ReactHow-ToBeginner · 3 min read

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 useCallback unnecessarily for functions that don't cause re-renders.
  • Expecting useCallback to 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 useCallback to 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.memo in 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.