React 45 ⚛️ Memoizing Callback Functions with useCallback
useCallback is the function-specific version of useMemo. It returns a memoized function that only changes identity when its dependencies change. The comparison is the same: on every render, React compares the dependency array, and if the dependencies are unchanged, it returns the same function reference from the previous render. If a dependency has changed, it returns the new function that was passed in, and caches it for the next render. The hook does not make the function run faster. It does not change what the function does. It only stabilizes the function’s identity across renders, so that components and effects that depend on the reference are not triggered unnecessarily.
The need for this is a direct consequence of how React compares props and dependencies. React.memo compares props by reference. An effect compares its dependencies by Object.is. A function defined inside a component is a new function on every render. The reference changes, React.memo sees a new prop, the effect sees a new dependency, and both re-run. useCallback breaks that cycle by returning the same function reference until the dependencies change. The memoized child does not re-render, and the effect does not re-run, because the reference it depends on has not changed.
The important nuance is that useCallback is not about the function’s contents. The function is recreated on every render regardless; the hook only decides which reference to return. On a render where the dependencies are unchanged, the new function is created and then discarded, and the previous function is returned. The cost of creating the function is not saved. What is saved is the downstream work: the re-render of a memoized child, or the re-run of an effect. This is why useCallback is only valuable when the function is passed to a memoized child, used as a dependency of an effect, or passed to a custom hook that depends on the reference. If the function is used only in event handlers on plain DOM elements, the reference does not matter, and the hook is unnecessary overhead.
This chapter covers three areas. First, why useCallback exists — the reference-equality problem with functions and the two downstream consumers that care about identity: React.memo and effect dependency arrays. Second, how useCallback works — the signature, the dependency array, the caching behavior, and the difference from useMemo. Third, when to use it and when not to — the criteria for a memoized child, the effect-dependency pattern, the custom-hook pattern, and the cases where a plain function is the right choice. The chapter ends with a complete example session, a quick reference, best practices, common pitfalls, real-world examples, and diagrams showing the callback identity and the memoization flow.
Key point: useCallback returns the same function reference across renders until its dependencies change. It does not make the function faster; it stabilizes the reference. Use it when the function is passed to a memoized child, used as an effect dependency, or passed to a custom hook that depends on identity. Do not use it for functions that are only used in event handlers on plain elements.
Why useCallback exists
The function-identity problem. Every time a component renders, the functions defined inside it are new. A function expression creates a new closure, and the closure’s reference is different from the previous render’s. This is not a bug; it is how JavaScript works. But React’s optimizations compare by reference. React.memo compares the props object of a child, and if any prop is a function, the comparison fails because the function reference is new. An effect compares its dependency array, and if a function is in the array, the effect re-runs because the function reference is new. The new function does the same thing as the old one, but React cannot know that. It only sees a different reference .
The React.memo problem. A child component wrapped in React.memo re-renders only when its props change. If a parent passes a function to the child, the function is a new reference on every parent render, so the child re-renders on every parent render. The memoization is defeated by the unstable function. useCallback solves this by returning the same function reference across renders, so the child’s props are unchanged, and React.memo skips the re-render. The child still renders when the function’s dependencies change, because that is when the reference changes .
The effect-dependency problem. An effect runs when its dependencies change. If a function is in the dependency array, the effect re-runs on every render, because the function is new on every render. This is usually not the intent. The effect should run when the function’s dependencies change, not when the component re-renders. useCallback stabilizes the function, so the effect’s dependency array is stable, and the effect runs only when the function’s dependencies change. This is the standard pattern for effects that call a function defined in the component .
The custom-hook problem. A custom hook that accepts a function and stores it, passes it to an effect, or uses it in a memoized value depends on the function’s identity. If the function is new on every render, the hook’s internal effects and memoizations are invalidated on every render. useCallback at the call site stabilizes the function, so the hook’s internals are stable. This is the pattern for hooks like useInterval, useEventListener, and any hook that subscribes to a callback .
The premature-optimization problem. useCallback is often added by default, before there is evidence of a problem. The React documentation is explicit: useCallback is a performance optimization, not a correctness tool. A component that uses it produces the same output as one that does not. The hook adds a dependency array and a memoization wrapper, and the code is harder to read. The recommendation is to add it when the profiler shows an unnecessary re-render, or when the function is passed to a memoized child that is expensive. The hook is not a default; it is a response to a measured problem .
The trade-off. useCallback has a cost. It stores the function and the dependencies, and it compares the dependencies on every render. For a function that is not passed to a memoized child and not used in an effect, the hook is pure overhead. The function is created on every render regardless; the hook only decides which reference to return. If no downstream consumer cares about the reference, the comparison is wasted. The trade-off is between the reference stability that useCallback provides and the overhead of the memoization. The benefit exists only when the stability matters.
a. The useCallback signature and behavior
useCallback takes two arguments: the function to memoize, and a dependency array. It returns the memoized function. On the first render, it returns the function and caches it along with the dependencies. On subsequent renders, it compares the dependencies with the previous values, and if they are unchanged, it returns the cached function. If any dependency has changed, it caches the new function and returns it .
import { useCallback } from 'react';
function Parent({ items, onSelect }) {
const handleSelect = useCallback(
(id) => {
onSelect(id);
},
[onSelect]
);
return <MemoizedList items={items} onSelect={handleSelect} />;
}
The handleSelect function is created on every render, but useCallback returns the cached version when onSelect is unchanged. The MemoizedList receives the same function reference across renders, so React.memo skips the re-render.
The dependency array must include every value from the component’s scope that the function reads. If the function reads onSelect, then onSelect must be in the array. If the function also reads a state variable, that variable must be in the array. A missing dependency causes the function to use a stale value; an extra dependency causes the function to change identity more often than necessary. The exhaustive-deps ESLint rule reports both cases .
An empty dependency array means the function is created once and never changes:
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
The function is stable for the lifetime of the component. It does not read any props or state, so it does not need to change. This is the pattern for a callback that is passed to a memoized child and does not depend on any changing values. If the function reads a state variable, the empty array is a bug: the function captures the initial value and never sees the updated one.
The comparison uses Object.is, the same as useMemo and useEffect. For primitives, it is value equality. For objects and functions, it is reference equality. A dependency that is an object or a function recreated on every render causes the callback to change on every render, which is the problem useCallback is meant to solve. The dependencies should be the primitive values that the function actually uses, or stable references that are themselves memoized .
b. useCallback vs useMemo
useCallback and useMemo are closely related. useCallback(fn, deps) is equivalent to useMemo(() => fn, deps). The difference is the intent: useMemo is for caching a value, and useCallback is for caching a function. The implementation is the same, and the dependency comparison is the same.
// These are equivalent:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);
The useCallback form is more readable when the value being memoized is a function. The useMemo form is more readable when the value is an object or a computed result. The choice is a matter of intent, and the React team recommends useCallback for functions and useMemo for values.
The two hooks compose. A useMemo that returns an object containing functions should use useCallback for the functions, so the object’s dependencies are stable:
const handlers = useMemo(
() => ({
onSelect: handleSelect,
onDelete: handleDelete,
}),
[handleSelect, handleDelete]
);
If handleSelect and handleDelete are memoized with useCallback, the handlers object is stable as long as its dependencies are stable. This is the pattern for passing a group of callbacks to a child without causing unnecessary re-renders.
The naming convention is also a signal. A function named handleX is an event handler; it is typically memoized with useCallback. A value named x or computedX is a derived value; it is typically memoized with useMemo. The two hooks are not interchangeable in intent, even though they are interchangeable in implementation.
c. When to use useCallback and when not to
The decision to use useCallback depends on whether the function’s identity matters to a downstream consumer. If nothing compares the function by reference, the hook is unnecessary.
Use useCallback when:
- The function is passed to a child component wrapped in
React.memo. The child re-renders only when its props change, and a stable function reference prevents unnecessary re-renders. - The function is used in the dependency array of a
useEffect. A stable reference prevents the effect from re-running on every render. This is the most common reason foruseCallback, because an effect that re-runs on every render can cause performance problems and, in some cases, infinite loops. - The function is passed to a custom hook that depends on the function’s identity. A hook like
useIntervaloruseEventListenerthat stores the callback and uses it in an effect needs a stable reference to avoid re-subscribing on every render. - The function is passed as a dependency to another memoized value, such as a
useMemothat returns an object containing the function.
Do not use useCallback when:
- The function is used only in event handlers on plain DOM elements, like
<button onClick={handleClick}>. The reference does not matter, because the button’sonClickprop is not compared by reference. The function is attached to the DOM, and React does not re-attach it on every render. - The function is used only in the component’s own logic, without being passed anywhere. The reference is irrelevant.
- The component does not re-render frequently. The memoization saves work only when the re-renders are frequent and the work is expensive.
- The function’s dependencies change on every render. If the dependency array includes an object or a function that is recreated on every render, the callback changes on every render, and the hook provides no benefit. The dependencies must be stabilized first.
The React documentation’s guidance is the same as for useMemo: measure first, memoize when there is a problem. The profiler shows which components re-render and why. If a memoized child is re-rendering because of an unstable function prop, useCallback is the fix. If no component is re-rendering unnecessarily, the hook is not needed .
Complete Example Session
// ============================================
// PART 1: THE PROBLEM — UNSTABLE CALLBACK
// ============================================
import { useState } from 'react';
const MemoizedChild = React.memo(function Child({ onAction }) {
console.log('Child rendered');
return <button onClick={onAction}>Action</button>;
});
function Parent() {
const [count, setCount] = useState(0);
// New function on every render
const handleAction = () => {
console.log('action');
};
return (
<>
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
<MemoizedChild onAction={handleAction} />
</>
);
}
// Every click on the count button re-renders Parent.
// handleAction is a new function.
// MemoizedChild receives a new prop and re-renders.
// React.memo is defeated.
// ============================================
// PART 2: THE FIX — useCallback
// ============================================
import { useState, useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
const handleAction = useCallback(() => {
console.log('action');
}, []);
return (
<>
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
<MemoizedChild onAction={handleAction} />
</>
);
}
// handleAction is stable across renders.
// MemoizedChild receives the same prop and skips the re-render.
// ============================================
// PART 3: THE EFFECT DEPENDENCY PATTERN
// ============================================
function SearchResults({ query }) {
const fetchResults = useCallback(async () => {
const res = await fetch(`/api/search?q=${query}`);
return res.json();
}, [query]);
useEffect(() => {
fetchResults().then((data) => console.log(data));
}, [fetchResults]);
return <div>Results for {query}</div>;
}
// fetchResults changes only when query changes.
// The effect runs only when query changes.
// Without useCallback, the effect would run on every render.
// ============================================
// PART 4: THE CUSTOM HOOK PATTERN
// ============================================
function useInterval(callback, delay) {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
if (delay === null) return;
const id = setInterval(() => savedCallback.current(), delay);
return () => clearInterval(id);
}, [delay]);
}
function Timer() {
const [count, setCount] = useState(0);
const tick = useCallback(() => {
setCount((c) => c + 1);
}, []);
useInterval(tick, 1000);
return <p>{count}</p>;
}
// tick is stable, so the interval is not reset on every render.
// The useInterval hook stores the latest callback in a ref.
// ============================================
// PART 5: THE DEPENDENCY PROBLEM
// ============================================
function Parent({ items }) {
const [filter, setFilter] = useState('');
// ❌ The dependency is an object recreated on every render
const options = { filter, limit: 10 };
const handleSelect = useCallback((id) => {
console.log(id, options);
}, [options]); // options changes on every render
// The callback changes on every render.
// useCallback provides no benefit.
return <MemoizedChild onSelect={handleSelect} />;
}
// ============================================
// PART 6: FIXING THE DEPENDENCY
// ============================================
function Parent({ items }) {
const [filter, setFilter] = useState('');
const handleSelect = useCallback((id) => {
console.log(id, filter);
}, [filter]); // primitive dependency
// The callback changes only when filter changes.
// useCallback works.
return <MemoizedChild onSelect={handleSelect} />;
}
// ============================================
// PART 7: WHEN NOT TO USE useCallback
// ============================================
function SimpleButton() {
const [count, setCount] = useState(0);
// ❌ Unnecessary: the function is used only
// on a plain DOM element, not passed to a memoized child.
const handleClick = useCallback(() => {
setCount((c) => c + 1);
}, []);
return <button onClick={handleClick}>{count}</button>;
}
// ✅ This is sufficient:
function SimpleButton() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount((c) => c + 1)}>{count}</button>;
}
// ============================================
// PART 8: useCallback vs useMemo
// ============================================
// These are equivalent:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);
// useCallback is for functions.
// useMemo is for values.
// ============================================
// PART 9: COMBINING useCallback WITH useMemo
// ============================================
function Parent({ items }) {
const handleSelect = useCallback((id) => {
console.log('selected', id);
}, []);
const handleDelete = useCallback((id) => {
console.log('deleted', id);
}, []);
const handlers = useMemo(
() => ({ onSelect: handleSelect, onDelete: handleDelete }),
[handleSelect, handleDelete]
);
return <MemoizedList items={items} handlers={handlers} />;
}
// handlers is stable because both callbacks are stable.
// The memoized list does not re-render unnecessarily.
// ============================================
// PART 10: THE COMPLETE EXAMPLE
// ============================================
import { useState, useCallback, useMemo } from 'react';
const MemoizedList = React.memo(function List({ items, onSelect }) {
console.log('List rendered');
return (
<ul>
{items.map((item) => (
<li key={item.id} onClick={() => onSelect(item.id)}>
{item.name}
</li>
))}
</ul>
);
});
function App() {
const [items, setItems] = useState([
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
]);
const [selected, setSelected] = useState(null);
// Stable callback: passed to MemoizedList
const handleSelect = useCallback((id) => {
setSelected(id);
}, []);
// Stable derived value: the visible items
const visibleItems = useMemo(
() => items.filter((i) => i.name !== 'hidden'),
[items]
);
return (
<>
<p>Selected: {selected}</p>
<MemoizedList items={visibleItems} onSelect={handleSelect} />
<button onClick={() => setSelected(null)}>Clear</button>
</>
);
}
// handleSelect is stable, so MemoizedList does not
// re-render when selected changes.
// visibleItems is stable when items is unchanged.
The ten parts show the unstable callback problem, the useCallback fix, the effect dependency pattern, the custom hook pattern, the dependency problem, fixing the dependency, when not to use useCallback, the useCallback vs useMemo comparison, combining the two, and the complete example.
Quick Reference
useCallback Signature
| Argument | Meaning |
|---|---|
| Function | The function to memoize |
| Dependency array | Values the function reads |
| Return value | The memoized function |
Dependency Array
| Array | Behavior |
|---|---|
[] | Function created once, stable forever |
[a, b] | Function changes when a or b changes |
| Missing | Function changes on every render |
| Object/function dependency | Function changes when the reference changes |
useCallback vs useMemo
| Aspect | useCallback | useMemo |
|---|---|---|
| Returns | A function | A value |
| Equivalent | useMemo(() => fn, deps) | useCallback(fn, deps) |
| Use for | Event handlers, callbacks | Computed values, objects |
| Naming | handleX | x, computedX |
When to Use useCallback
| Condition | Use useCallback? |
|---|---|
Passed to React.memo child | Yes |
Used in useEffect dependency array | Yes |
| Passed to a custom hook that depends on identity | Yes |
Used as a dependency of a useMemo | Yes |
| Used only on a plain DOM element | No |
| Used only in the component’s own logic | No |
| Component re-renders rarely | No |
Best Practices
✅ Do This:
// Memoize a callback passed to a memoized child
const handleSelect = useCallback((id) => { ... }, []); // ✅
// Memoize a function used in an effect dependency
const fetchData = useCallback(() => { ... }, [query]); // ✅
// Use useCallback for stable callbacks passed to custom hooks
const tick = useCallback(() => setCount((c) => c + 1), []); // ✅
// Use primitive dependencies
useCallback(() => doSomething(id), [id]); // ✅
// Combine with useMemo for stable objects
const handlers = useMemo(() => ({ onSelect, onDelete }), [onSelect, onDelete]); // ✅
❌ Don’t Do This:
// Don't memoize a callback used only on a plain element
const handleClick = useCallback(() => setCount(c => c + 1), []); // ❌
// Don't use an object dependency recreated on every render
useCallback(() => { ... }, [{ filter, limit }]); // unstable // ❌
// Don't omit dependencies
useCallback(() => doSomething(a, b), [a]); // b is stale // ❌
// Don't use useCallback by default
// Add it when the profiler shows a problem // ❌
// Don't use useCallback for side effects
useCallback(() => { fetch('/api'); }, []); // use useEffect // ❌
// Don't wrap every function in useCallback
function handleClick() { ... } // plain function is fine // ✅
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Memoized child still re-renders | Another prop is unstable | Stabilize all props |
| Callback changes on every render | Object dependency recreated | Use primitive dependencies |
| Stale value in callback | Missing dependency | Add the dependency |
| Effect runs on every render | Callback not memoized | Wrap in useCallback |
| No performance benefit | Function used only on a plain element | Remove useCallback |
| Custom hook re-subscribes | Callback changes on every render | Memoize the callback |
| Infinite effect loop | Callback changes, effect sets state | Memoize the callback and its dependencies |
Real-World Examples
1. Memoized Child Callback
const handleSelect = useCallback((id) => setSelected(id), []);
2. Effect Dependency
const fetchData = useCallback(() => { ... }, [query]);
useEffect(() => { fetchData(); }, [fetchData]);
3. Custom Hook Callback
const tick = useCallback(() => setCount((c) => c + 1), []);
useInterval(tick, 1000);
4. Stable Object of Callbacks
const handlers = useMemo(() => ({ onSelect, onDelete }), [onSelect, onDelete]);
5. Event Handler with Dependency
const handleChange = useCallback((e) => setValue(e.target.value), []);
6. Empty Dependency Array
const handleClose = useCallback(() => setOpen(false), []);
7. Callback with Prop Dependency
const handleSubmit = useCallback(() => onSubmit(formData), [onSubmit, formData]);
8. Ref-Based Stable Callback
const savedCallback = useRef(callback);
useEffect(() => { savedCallback.current = callback; }, [callback]);
9. Interval with Stable Callback
const tick = useCallback(() => { ... }, []);
useInterval(tick, 1000);
10. Complete Memoized Component
const MemoizedList = React.memo(function List({ items, onSelect }) { ... });
const handleSelect = useCallback((id) => { ... }, []);
<MemoizedList items={items} onSelect={handleSelect} />
Visual
The Callback Identity Problem
┌──────────────────────────────────────────────────────────────┐
│ THE CALLBACK IDENTITY PROBLEM │
│ │
│ WITHOUT useCallback: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Render 1: │ │
│ │ const handleClick = () => { ... } ← ref A │ │
│ │ <Child onClick={handleClick} /> │ │
│ │ │ │
│ │ Render 2 (unrelated state change): │ │
│ │ const handleClick = () => { ... } ← ref B │ │
│ │ <Child onClick={handleClick} /> │ │
│ │ │ │
│ │ Child compares props: │ │
│ │ onClick: ref A !== ref B → re-render │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ WITH useCallback: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Render 1: │ │
│ │ const handleClick = useCallback(fn, []) ← ref A │ │
│ │ │ │
│ │ Render 2: │ │
│ │ useCallback returns ref A (deps unchanged) │ │
│ │ │ │
│ │ Child compares props: │ │
│ │ onClick: ref A === ref A → skip re-render │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
The Effect Dependency Problem
┌──────────────────────────────────────────────────────────────┐
│ THE EFFECT DEPENDENCY PROBLEM │
│ │
│ WITHOUT useCallback: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ function Search({ query }) { │ │
│ │ const fetchData = () => { ... }; ← new every render│ │
│ │ │ │
│ │ useEffect(() => { │ │
│ │ fetchData(); │ │
│ │ }, [fetchData]); ← new dependency every render │ │
│ │ } │ │
│ │ │ │
│ │ Effect runs on EVERY render. │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ WITH useCallback: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ function Search({ query }) { │ │
│ │ const fetchData = useCallback(() => { ... }, [query])│ │
│ │ │ │
│ │ useEffect(() => { │ │
│ │ fetchData(); │ │
│ │ }, [fetchData]); ← stable unless query changes │ │
│ │ } │ │
│ │ │ │
│ │ Effect runs only when query changes. │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
The Decision Flow
┌──────────────────────────────────────────────────────────────┐
│ useCallback DECISION FLOW │
│ │
│ Is the function passed to a React.memo child? │
│ │ │
│ ├── YES ──► USE useCallback │
│ │ │
│ └── NO ──► Is the function used in an effect dependency? │
│ │ │
│ ├── YES ──► USE useCallback │
│ │ │
│ └── NO ──► Is the function passed to a │
│ custom hook that depends on │
│ identity? │
│ │ │
│ ├── YES ──► USE useCallback │
│ │ │
│ └── NO ──► Do NOT use useCallback │
│ (plain function is fine)│
│ │
│ Always measure with React DevTools Profiler first. │
│ │
└──────────────────────────────────────────────────────────────┘
useCallback vs useMemo
┌──────────────────────────────────────────────────────────────┐
│ useCallback vs useMemo │
│ │
│ useCallback: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ const handleClick = useCallback(() => { ... }, [a]) │ │
│ │ │ │
│ │ Returns a FUNCTION │ │
│ │ Used for event handlers and callbacks │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ useMemo: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ const value = useMemo(() => compute(a), [a]) │ │
│ │ │ │
│ │ Returns a VALUE │ │
│ │ Used for computed values and objects │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Equivalence: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ useCallback(fn, deps) │ │
│ │ ≡ useMemo(() => fn, deps) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ The same mechanism; different intent. │
│ │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
useCallback | Returns a memoized function |
| Signature | useCallback(fn, deps) |
| Dependency array | Values the function reads |
| Comparison | Object.is |
| Empty array | Function created once, stable forever |
| Equivalent to | useMemo(() => fn, deps) |
| Use for | Memoized children, effect dependencies, custom hooks |
| Not for | Plain DOM handlers, default habit |
| Linter | exhaustive-deps |
| Measurement | React DevTools Profiler |
Key takeaways:
useCallbackstabilizes a function’s reference. It returns the same function across renders until its dependencies change. It does not make the function run faster; it only prevents downstream consumers from seeing a new reference .- The function is recreated on every render regardless. The hook decides which reference to return. On a render where the dependencies are unchanged, the new function is created and discarded, and the cached function is returned. The cost of creating the function is not saved; the downstream work is .
- Use it when the function’s identity matters. The two main cases are a function passed to a
React.memochild and a function used in auseEffectdependency array. A custom hook that depends on the function’s identity is the third case . - Do not use it for plain DOM event handlers. A function passed to
<button onClick={...}>is not compared by reference. The reference does not matter, and theuseCallbackis pure overhead . - The dependency array must include every value the function reads. A missing dependency causes a stale value. An object or function dependency recreated on every render causes the callback to change on every render, which defeats the purpose .
useCallback(fn, deps)is equivalent touseMemo(() => fn, deps). The two hooks share the same mechanism. UseuseCallbackfor functions anduseMemofor values, as a matter of intent and readability .- Memoize the dependencies first. If a callback depends on an object that is recreated on every render,
useCallbackprovides no benefit. The object must be stabilized withuseMemobefore the callback can be stable . - Measure before optimizing.
useCallbackadds code complexity and a dependency array. It should be added when the profiler shows an unnecessary re-render, not by default .
Remember: useCallback is the function-specific version of useMemo. It returns the same function reference across renders until its dependencies change, which prevents memoized children from re-rendering and effects from re-running. It does not make the function faster; it makes the function stable. Use it when the function is passed to a React.memo child, used in an effect dependency, or passed to a custom hook that depends on identity. Do not use it for functions that are only used on plain DOM elements or in the component’s own logic. The dependency array must include every value the function reads, and the dependencies must themselves be stable. Measure first, memoize when there is a problem.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!