- React Tutorial
- React Home
- React Setup
- React Introduction
- React ES6
- React Render HTML
- React JSX
- React Components
- React Class
- React Props
- React Events
- React Conditional
- React Lists
- React Forms
- React Router
- React Memo
- React CSS Styling
- React Hooks
- What is a Hook?
- React useState
- React useEffect
- React useContext
- React useRef
- React useReducer
- React useCallback
- React useMemo
- React Custom Hooks
React useCallback Hook
- useCallback is a React hook that memoizes a callback function. In simple terms, it "remembers" your function between renders so it isn't recreated from scratch every time your component updates.
- In JavaScript, functions are objects. Every time a component re-renders, any function defined inside it is technically a "new" function instance, even if the code inside hasn't changed.
useCallbackhelps maintain referential equality for these functions.
Developer Tip: Think of
useCallback as a way to give your function a stable identity. This is crucial when that function is used as a dependency in other hooks or passed to optimized child components.
Purpose
- Prevent Unnecessary Re-renders: When you pass a function to a child component that is wrapped in
React.memo, that child will only re-render if its props change. WithoutuseCallback, the function prop looks "new" on every render, forcing the child to re-render needlessly. - Stable Dependencies: If a function is used inside a
useEffector anotheruseMemo, providing a stable reference prevents those hooks from triggering an infinite loop or running more often than they should. - Performance Optimization: It reduces the overhead of garbage collection by not creating thousands of short-lived function objects in complex, frequently updating interfaces.
Best Practice: Don't wrap every single function in
useCallback. It adds its own overhead. Use it specifically when passing functions to memoized components or when the function is a dependency in another hook.
Syntax
- Import useCallback from the 'react' library.
- Pass two arguments: the function you want to memoize, and a dependency array. The function will only be recreated if one of the values in the dependency array changes.
import React, { useCallback } from 'react';
const memoizedCallback = useCallback(
() => {
// Your logic here
console.log("This function is stable!");
},
[dependency1, dependency2] // Only recreate if these change
);
Common Mistake: Forgetting the dependency array entirely or leaving out variables used inside the function. If you use a variable inside the callback but don't include it in the array, the function will "capture" an old version of that variable (a stale closure).
Memoized Callback
- Use useCallback to ensure a function maintains its reference across renders, which is especially useful when updating state based on previous values.
import React, { useState, useCallback } from 'react';
const MemoizedCallbackExample = () => {
const [count, setCount] = useState(0);
// By using the "functional update" pattern (prevCount => prevCount + 1),
// we can keep the dependency array empty [].
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []);
return (
<div>
<h2>Example: Memoized Callback</h2>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
Developer Tip: Using the functional update
setCount(c => c + 1) is a great trick. It allows you to remove the count state from your useCallback dependencies, making the function even more stable!
Passing Callback to Child Component
- This is the most common real-world use case. It ensures that a child component doesn't re-render unless the logic of the callback actually needs to change.
import React, { useState, useCallback } from 'react';
// Imagine ChildComponent is expensive to render
const ChildComponent = React.memo(({ onClick }) => {
console.log("ChildComponent rendered!");
return <button onClick={onClick}>Click Me</button>;
});
const ParentComponent = () => {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
// increment is memoized. It won't change even if 'text' state updates.
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []);
return (
<div>
<h2>Example: Passing Callback to Child Component</h2>
<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Type something..."
/>
<p>Count: {count}</p>
{/* ChildComponent will NOT re-render when you type in the input
because 'increment' is memoized! */}
<ChildComponent onClick={increment} />
</div>
);
};
Watch Out:
useCallback does almost nothing on its own if the child component is not wrapped in React.memo. Standard components re-render whenever their parent does, regardless of whether the props are technically the "same" instance.
Summary
The useCallback hook is a powerful tool for optimizing React applications. By preserving the identity of a function between renders, you prevent unnecessary downstream updates and keep your dependency chains clean. While it isn't necessary for every function, it is essential when building high-performance lists, complex forms, or components that rely on strict referential equality to function correctly.