useCallback
Memoize a function so its identity stays stable across renders.
const handler = useCallback(() => doThing(id), [id]);useCallback returns a memoized function that keeps the same identity between renders as long as its dependencies do not change. It is essentially useMemo for functions.
Why function identity matters
Every render creates new function objects. If you pass a callback to a component wrapped in memo, a fresh function each render would defeat the memoization. useCallback keeps the reference stable so the child can skip re-rendering.
Example
import { useCallback, useState } from 'react';
import { memo } from 'react';
const Child = memo(function Child({ onClick }) {
return <button onClick={onClick}>Click</button>;
});
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount((c) => c + 1);
}, []);
return (
<div>
<p>{count}</p>
<Child onClick={handleClick} />
</div>
);
}When to use it
- A memoized list item component receives an onDelete callback; useCallback keeps the callback reference stable so the item does not re-render every time the parent state changes.
- A custom useDebounce hook wraps the debounced function in useCallback so callers receive a stable reference that can safely be added to dependency arrays.
- A paginated table passes a fetchPage callback to a child pagination control; useCallback prevents the control from re-rendering whenever unrelated parent state updates.
More examples
Stable callback for memoized child
useCallback gives handleDelete a stable identity so the memo'd ListItem is not re-rendered when filter changes.
import { useState, useCallback, memo } from 'react';
const ListItem = memo(({ item, onDelete }) => (
<li>{item.name} <button onClick={() => onDelete(item.id)}>X</button></li>
));
function List({ items }) {
const [filter, setFilter] = useState('');
const handleDelete = useCallback(id => {
console.log('delete', id);
}, []);
return (
<>
<input value={filter} onChange={e => setFilter(e.target.value)} />
<ul>{items.map(i => <ListItem key={i.id} item={i} onDelete={handleDelete} />)}</ul>
</>
);
}Callback with dependencies
Memoizes a callback that captures userId; the function is recreated only when onSearch or userId changes.
import { useCallback } from 'react';
function SearchForm({ onSearch, userId }) {
const handleSubmit = useCallback(
(query) => {
onSearch({ userId, query });
},
[onSearch, userId]
);
return <SearchInput onSubmit={handleSubmit} />;
}useCallback vs useMemo equivalence
Shows that useCallback is syntactic sugar over useMemo returning a function, clarifying when to use each.
import { useCallback, useMemo } from 'react';
// These are equivalent:
const handleClick = useCallback(() => doSomething(x), [x]);
// same as:
const handleClickAlt = useMemo(() => () => doSomething(x), [x]);
// Prefer useCallback for functions β it is clearer in intent.
Discussion