reactjs
/

React useCallback Hook – Memoizing Functions

Last Sync: Today

On this page

6
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

reactjs

React useCallback Hook – Memoizing Functions

What is useCallback?

The useCallback hook is a React hook that returns a memoized version of a callback function. It only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

The Problem: Referential Equality

In JavaScript, a function defined inside a component is recreated on every render. Even if the function logic is identical, the memory reference changes. If you pass this function as a prop to a child component wrapped in React.memo, that child will re-render every time because it sees a 'new' prop.

React JSXRead-only
1
import { useState, useCallback } from 'react';
import ChildComponent from './ChildComponent';

function Parent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  // ❌ Without useCallback: recreated on every render
  // const handleClick = () => { console.log('Clicked'); };

  // ✅ With useCallback: memoized until dependencies change
  const handleClick = useCallback(() => {
    console.log('Button clicked! Count is:', count);
  }, [count]); // Only recreate if count changes

  return (
    <>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <ChildComponent onClick={handleClick} />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </>
  );
}

When to Use useCallback

  • Passing callbacks to optimized child components (using React.memo).
  • When the function is a dependency for other hooks like useEffect.
  • Preventing 'Effect' loops: If a function is defined in the body and used in useEffect, it will trigger the effect on every render unless memoized.
  • Custom Hooks: When returning functions from custom hooks so consumers can safely use them in their own dependency arrays.

useCallback vs. useMemo

While both hooks are for memoization, they serve different purposes:

HookReturnsPrimary Use Case
useCallbackThe function itself (memoized)Preventing function recreation for props/deps
useMemoThe result of a function (memoized value)Expensive calculations or object/array stability

Common Mistake: Over-optimization

Don't wrap every function in useCallback. It has its own cost (memory for the dependency array and the hook call itself). If a component is cheap to re-render, or if you aren't passing the function to a React.memo component, the overhead of useCallback might be greater than the benefit.

Best Practices

  • Always include all dependencies used inside the function in the dependency array.
  • Use functional updates (e.g., setCount(c => c + 1)) to reduce the number of dependencies required.
  • Pair with React.memo: useCallback is mostly useless unless the child component receiving the prop is also memoized.

Try it yourself

import React, { useState, useCallback } from 'react';

// A memoized child component
const ExpensiveList = React.memo(({ onItemClick }) => {
  console.log('--- Child Rendering ---');
  const items = [1, 2, 3, 4, 5];
  return (
    <ul>
      {items.map(i => (
        <li key={i} onClick={() => onItemClick(i)} style={{ cursor: 'pointer' }}>
          Item {i} (Click me)
        </li>
      ))}
    </ul>
  );
});

function App() {
  const [count, setCount] = useState(0);
  const [input, setInput] = useState('');

  // Try removing 'useCallback' and wrapping only the arrow function.
  // You'll notice the Child renders on every keystroke in the input!
  const handleItemClick = useCallback((id) => {
    alert('Clicked item ' + id);
  }, []);

  return (
    <div style={{ padding: '20px' }}>
      <h2>useCallback & React.memo</h2>
      <p>Count: {count} <button onClick={() => setCount(c => c + 1)}>+</button></p>
      
      <input 
        placeholder="Type to re-render parent..." 
        value={input} 
        onChange={e => setInput(e.target.value)} 
      />
      
      <p>Open the console. Notice the child doesn't re-render when you type, thanks to useCallback!</p>
      <ExpensiveList onItemClick={handleItemClick} />
    </div>
  );
}

export default App;

Test Your Knowledge

Q1
of 3

What does useCallback return?

A
A boolean value
B
The result of the callback function
C
A memoized version of the function itself
D
A promise
Q2
of 3

When is useCallback most effective?

A
When the function contains a loop
B
When passing props to a component wrapped in React.memo
C
To make API calls faster
D
To replace useState
Q3
of 3

If you use a variable inside useCallback but leave it out of the dependency array, what happens?

A
The code crashes
B
The function uses the 'stale' value from the first render
C
React automatically adds it for you
D
The function updates every time anyway

Frequently Asked Questions

Why is my child component still re-rendering?

Ensure the child is wrapped in React.memo(Component). If it isn't, it will re-render whenever the parent does, regardless of useCallback.

Can I use useCallback without a dependency array?

No. Without an array, it will recreate the function on every render, defeating the purpose. Use [] for a function that never changes.

Does useCallback make my function run faster?

No, it actually adds a tiny bit of overhead. Its purpose is to skip re-renders of other components or prevent unnecessary effect triggers.

Previous

react use memo

Next

react lifecycle

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.