- Animation
- Bootstrap
- Button
- Components
- Conditional rendering
- Context API
- Debounce
- Error boundary
- Events
- Form
- Fragment
- Hooks
- Inline styling
- Key
- Lazy loading
- Lifecycle methods
- Portal
- Prop types
- Props
- Redux library
- Ref
- Router
- State
- State management
- Strict mode
- Suspense
- useCallback Hook
- useEffect hook
- useMemo hook
- useReducer hook
- useRef hook
- Virtual DOM
REACT
React useCallback Hook: Syntax, Usage, and Examples
The useCallback
hook in React returns a memoized version of a callback function. It helps prevent unnecessary re-renders by keeping the same function instance between renders unless its dependencies change.
How to Use useCallback in React
To use useCallback
, you wrap your callback function inside the hook and pass in a dependency array. The syntax looks like this:
const memoizedCallback = useCallback(() => {
// function logic
}, [dependency1, dependency2]);
useCallback
: React hook that memoizes your function- Function: The actual callback you want to optimize
- Dependency array: List of values that will trigger a new function instance if they change
Here's a basic example:
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []);
In this case, handleClick
will not be recreated on re-renders unless one of the dependencies changes. Since the dependency array is empty, it remains the same for the lifetime of the component.
When to Use useCallback in React
The useCallback
hook is a performance optimization tool. It’s not necessary in every component, but it becomes useful in the following situations:
Passing Stable Callbacks to Child Components
When you pass a function to a child component, React re-creates it every time the parent re-renders. If the child uses React.memo
, this behavior can cause unnecessary renders. Using useCallback
prevents this.
const onChange = useCallback((value) => {
setState(value);
}, [setState]);
<MyInput onChange={onChange} />
In this example, onChange
won't change unless setState
changes. That keeps MyInput
from re-rendering when it doesn’t need to.
Functions Inside useEffect
If you define a function inside a useEffect
or pass a callback to it, the reference changes on each render unless you use useCallback
. This can lead to infinite loops or missed updates.
useEffect(() => {
const id = setInterval(() => {
console.log('Tick');
}, 1000);
return () => clearInterval(id);
}, [/* dependencies */]);
If you used a callback inside useEffect
, you’d want to memoize it with useCallback
so the effect doesn’t re-run unnecessarily.
Optimizing Lists and Event Handlers
When rendering lists of items, each with an event handler like onClick
, useCallback prevents each item from creating a new function instance, improving rendering performance.
const handleItemClick = useCallback((id) => {
console.log(`Clicked item ${id}`);
}, []);
Using the same handleItemClick
function reference helps React reuse DOM elements when diffing.
Examples of useCallback in React
Below are a few common scenarios where useCallback
improves performance and predictability.
Example 1: Memoized Event Handler
import React, { useState, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
return (
<button onClick={increment}>
Count: {count}
</button>
);
}
The increment
function stays the same between renders, avoiding unnecessary re-binding.
Example 2: Preventing Unnecessary Child Renders
import React, { useState, useCallback } from 'react';
import Child from './Child';
function Parent() {
const [text, setText] = useState('');
const handleTextChange = useCallback((e) => {
setText(e.target.value);
}, []);
return (
<><input onChange={handleTextChange} value={text} />
<Child onClick={handleTextChange} />
</>
);
}
If Child
is wrapped in React.memo
, it only re-renders if its props change. Since handleTextChange
is stable, unnecessary re-renders are avoided.
Example 3: Async useCallback in React
You can use async functions with useCallback
, but be aware they always return a Promise.
const fetchData = useCallback(async () => {
const response = await fetch('/api/data');
const result = await response.json();
console.log(result);
}, []);
This pattern is useful when passing async callbacks to event handlers or effects that need stable references.
Learn More About useCallback in React
How to Use useCallback in React with Dependencies
The dependency array is essential. If you leave it out or include unstable values, useCallback
won’t behave as expected. Only include values that are used inside the callback and that may change over time.
const handleSubmit = useCallback(() => {
doSomething(userInput); // userInput is a dependency
}, [userInput]);
If userInput
changes, a new version of handleSubmit
will be created.
React useMemo vs useCallback
A common point of confusion is the difference between react usememo vs usecallback
. Both are performance tools, but they serve different purposes.
React usecallback
memoizes a function.React usememo
memoizes a computed value.
So if you’re optimizing a value, use useMemo
:
const sortedList = useMemo(() => {
return [...list].sort();
}, [list]);
If you’re optimizing a function, use useCallback
:
const handleSort = useCallback(() => {
setList([...list].sort());
}, [list]);
Both tools reduce unnecessary computation or re-renders, but they solve different problems.
Comparing Function Instances
React compares function references using shallow equality. Without useCallback
, every new render creates a new function object, even if the logic doesn’t change. That’s why child components wrapped in React.memo
can suffer from “prop changes” unless the callback is stabilized.
// BAD: New function every render
<Child onClick={() => setValue(5)} />
// GOOD: Stable function
const handleClick = useCallback(() => setValue(5), []);
<Child onClick={handleClick} />
Pitfalls and Misuse
useCallback
is not always helpful. Overusing it can add complexity without real performance gain. Use it when:
- The function is passed to a memoized child
- The function is used in an effect dependency
- The function is expensive to recreate
Avoid it for internal functions that aren’t memoized or passed around. The cost of using useCallback
outweighs the benefits if there’s no re-render concern.
Integration with useEffect and useMemo
Sometimes you'll see a pattern where useCallback
and useMemo
are used together, especially in complex components.
const fetchData = useCallback(async () => {
const res = await fetch('/api/data');
return await res.json();
}, []);
const processedData = useMemo(() => {
return heavyComputation(rawData);
}, [rawData]);
Both tools work well together. One stabilizes a function, the other stabilizes a value.
Handling Dependencies in Async useCallback React
When working with asynchronous code in async usecallback react
, remember to manage stale closures. React doesn't cancel an ongoing async function if dependencies change, so be cautious with long-running operations.
const fetchData = useCallback(async () => {
let canceled = false;
const result = await fetchDataFromApi();
if (!canceled) {
setData(result);
}
return () => {
canceled = true;
};
}, []);
Adding cancellation logic helps prevent outdated data from overwriting current state.
React useCallback
is a precision tool in your performance toolkit. It doesn’t make your app faster by default but helps avoid wasted renders and expensive operations when used correctly. With practice, you’ll develop a sense for when it's worth reaching for this hook—and when to just let React handle it.
Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.