useCallback
useCallback in React covers caching function references for memoized children, Hook dependencies, custom Hook APIs, and intentional performance optimization.
useCallback is a React Hook that lets you cache a function definition between renders.
It is mainly useful when a function's identity matters—for example, when passing a callback to a memoized child component or when a function is used as a dependency of another Hook.
The basic pattern is:
const cachedFunction = useCallback(() => {// function logic},[dependencies]);
A useful mental model is:
Component renders↓Does the dependency list change?│┌───┴───┐│ │No Yes│ │Reuse Createsame newfunction function
The most important point is:
useCallback memoizes a function reference, not the result of calling that function.
Why Does Function Identity Matter?
In JavaScript, functions are objects and therefore have identity.
Consider:
function App() {const handleClick = () => {console.log("Clicked");};return <button onClick={handleClick}>Click</button>;}
Whenever App renders, the function expression can create a new function:
Render 1 → handleClick → Function ARender 2 → handleClick → Function BRender 3 → handleClick → Function C
Even though the function does the same thing, these are different references.
Function A !== Function B
This matters when another part of the application cares about reference equality.
Importing useCallback
Import it from React:
import { useCallback } from "react";
It can be used with other Hooks:
import {memo,useCallback,useState} from "react";
Basic Syntax
The basic syntax is:
const handleClick = useCallback(() => {// logic},[]);
With dependencies:
const handleClick = useCallback(() => {doSomething(userId);},[userId]);
The function is recreated when its dependencies change.
useCallback Returns a Function
This is important.
const handleClick = useCallback(() => {console.log("Hello");},[]);
The result of useCallback is the function itself.
You can pass it:
<button onClick={handleClick}>Click</button>
You can also call it manually:
handleClick();
The Hook does not execute the function for you.
useCallback vs useMemo
These Hooks are closely related.
useMemo
useMemo caches the result of a calculation:
const total = useMemo(() => calculateTotal(items),[items]);
useCallback
useCallback caches the function itself:
const handleDelete = useCallback((id) => {deleteItem(id);},[]);
Mental model:
useMemo↓"Remember this value."useCallback↓"Remember this function."
Conceptually, these are related:
useCallback(fn, dependencies)
can be thought of as roughly equivalent to:
useMemo(() => fn, dependencies)
The dedicated useCallback API makes the intent clearer when what you want to preserve is a function reference.
useCallback Does Not Cache the Function's Result
Consider:
const calculateTotal = useCallback(() => {return calculateExpensiveTotal(items);}, [items]);
This does not cache the total.
It caches the function:
calculateTotal function
When you call:
calculateTotal();
the calculation still executes.
If you want to cache the result:
const total = useMemo(() => calculateExpensiveTotal(items),[items]);
Use the correct Hook for the problem.
Why Functions Change Between Renders
Consider:
function Parent() {const [count, setCount] = useState(0);const handleClick = () => {console.log("Clicked");};return (<div><p>{count}</p><button onClick={() => setCount(count + 1)}>Increment</button><Child onClick={handleClick} /></div>);}
When count changes:
Parent renders↓New handleClick function↓Child receives new function reference
Even though:
handleClick
does exactly the same thing, its identity changed.
This becomes important when Child is optimized with memo.
React.memo and useCallback
Consider a memoized child:
const Child = memo(function Child({ onClick }) {console.log("Child rendered");return (<button onClick={onClick}>Child Button</button>);});
Now:
function Parent() {const [count, setCount] = useState(0);const handleClick = () => {console.log("Clicked");};return (<div><button onClick={() => setCount(count + 1)}>Count: {count}</button><Child onClick={handleClick} /></div>);}
Every parent render creates a new handleClick.
Therefore:
Parent renders↓New handleClick reference↓Child receives changed prop↓memo cannot skip Child
Using useCallback:
const handleClick = useCallback(() => {console.log("Clicked");}, []);
Now the function reference can remain stable:
Parent renders↓handleClick reference unchanged↓Child receives same function prop↓memo can skip unnecessary render
This is one of the most important real-world uses of useCallback.
Complete Example
import { memo, useCallback, useState } from "react";const Child = memo(function Child({ onClick }) {console.log("Child rendered");return (<button onClick={onClick}>Child Button</button>);});function Parent() {const [count, setCount] = useState(0);const handleChildClick = useCallback(() => {console.log("Child clicked");}, []);return (<div><p>Count: {count}</p><buttononClick={() =>setCount(previous => previous + 1)}>Increment</button><Child onClick={handleChildClick} /></div>);}export default Parent;
When count changes:
Parent renders↓count changes↓handleChildClick remains same↓Child receives same onClick reference↓memo can skip Child
useCallback Without memo
Consider:
function Parent() {const handleClick = useCallback(() => {console.log("Clicked");}, []);return (<button onClick={handleClick}>Click</button>);}
This is valid.
But it may provide no meaningful benefit.
Why?
Because the button is not a memoized child that needs a stable callback reference.
A new function would usually be completely fine:
function Parent() {const handleClick = () => {console.log("Clicked");};return (<button onClick={handleClick}>Click</button>);}
Therefore:
useCallback is not something you should automatically add to every event handler.
useCallback Is About Referential Equality
Suppose:
const first = () => {};const second = () => {};
Even though they contain identical logic:
first === second
is:
false
With useCallback, React can preserve the same function reference between renders while dependencies remain unchanged.
Conceptually:
Render 1↓Function ARender 2↓Same dependencies↓Function A reusedRender 3↓Dependency changes↓Function B created
This stable identity can matter for:
memo- Effect dependencies
- Custom Hooks
- Third-party integrations
- Subscription management
useCallback With Dependencies
Consider:
function UserProfile({ userId }) {const handleEdit = useCallback(() => {editUser(userId);}, [userId]);return (<button onClick={handleEdit}>Edit</button>);}
The callback uses:
userId
Therefore:
[userId]
is a dependency.
Mental model:
userId unchanged↓Reuse callbackuserId changed↓Create new callback
Missing Dependencies
A common mistake is:
const handleEdit = useCallback(() => {editUser(userId);}, []);
The callback reads:
userId
but the dependency array is empty.
This can cause the callback to use an outdated value.
Prefer:
const handleEdit = useCallback(() => {editUser(userId);}, [userId]);
Dependencies should accurately represent the reactive values used by the callback.
Functional State Updates and useCallback
Sometimes a callback can avoid depending on current state by using a functional state update.
Consider:
function Counter() {const [count, setCount] = useState(0);const increment = useCallback(() => {setCount(count + 1);}, [count]);return (<button onClick={increment}>{count}</button>);}
The callback depends on:
count
so its identity changes whenever count changes.
If the update only needs the previous state, use a functional updater:
const increment = useCallback(() => {setCount(previous => previous + 1);}, []);
Now the callback does not need the current count value.
This can make its identity stable.
Why This Pattern Is Useful
Compare:
const increment = useCallback(() => {setCount(count + 1);}, [count]);
with:
const increment = useCallback(() => {setCount(previous => previous + 1);}, []);
The second version says:
I do not need the current render's count.I only need the previous state supplied by React.
This can reduce dependencies and make the callback stable.
useCallback and Event Handlers
Most event handlers do not require useCallback.
Simple:
function Form() {function handleSubmit(event) {event.preventDefault();}return (<form onSubmit={handleSubmit}>...</form>);}
There is usually no reason to memoize this function unless its reference stability matters.
Use:
useCallback(...)
when there is a concrete reason.
useCallback With memoized Children
This is the most common optimization pattern.
Parent:
const handleDelete = useCallback((id) => {deleteItem(id);},[]);
Child:
const TodoItem = memo(function TodoItem({todo,onDelete}) {return (<div><span>{todo.text}</span><button onClick={() => onDelete(todo.id)}>Delete</button></div>);});
The stable onDelete reference can help memo skip renders when the child's other props are unchanged.
Important: memo and useCallback Work Together
useCallback by itself does not stop a child from rendering.
For example:
const handleClick = useCallback(() => {},[]);return <Child onClick={handleClick} />;
If Child is not optimized and its parent renders, the child can still render.
The optimization is often:
useCallback+memo↓Stable function prop↓Child can skip unnecessary rendering
Example With a Large List
Suppose:
const TodoItem = memo(function TodoItem({todo,onDelete}) {return (<li>{todo.text}<button onClick={() => onDelete(todo.id)}>Delete</button></li>);});
Parent:
function TodoList({ todos }) {const handleDelete = useCallback((id) => {// delete todo}, []);return (<ul>{todos.map(todo => (<TodoItemkey={todo.id}todo={todo}onDelete={handleDelete}/>))}</ul>);}
A stable callback can prevent every TodoItem from receiving a new function reference when unrelated parent state changes.
This matters more when:
- The list is large.
- Child rendering is expensive.
- Parent updates frequently.
useCallback With useEffect
A callback can also be a dependency of an effect.
For example:
function Chat({ roomId }) {const createOptions = useCallback(() => {return {roomId};}, [roomId]);useEffect(() => {const options = createOptions();connect(options);}, [createOptions]);return <div>Chat</div>;}
Because createOptions is memoized, the effect can depend on a stable callback reference.
However, this does not automatically mean useCallback is the best design.
Often the function can simply be moved inside the effect:
useEffect(() => {const options = {roomId};connect(options);}, [roomId]);
This is simpler.
The important lesson is:
Do not introduce useCallback merely to satisfy an effect dependency if the function can be structured more clearly without it.
Avoid Unnecessary Callback Dependencies
Consider:
const createOptions = useCallback(() => {return {roomId};}, [roomId]);useEffect(() => {const options = createOptions();connect(options);}, [createOptions]);
This works, but may be unnecessarily complicated.
Prefer:
useEffect(() => {const options = {roomId};connect(options);}, [roomId]);
The effect now directly expresses what it synchronizes with.
useCallback and Custom Hooks
Custom Hooks sometimes return callback functions.
For example:
function useCounter() {const [count, setCount] = useState(0);const increment = useCallback(() => {setCount(previous => previous + 1);}, []);return {count,increment};}
A component can use:
function Counter() {const {count,increment} = useCounter();return (<button onClick={increment}>{count}</button>);}
A stable callback can be useful when consumers depend on its identity.
Custom Hooks will be covered later in the course.
useCallback and Context
Functions are sometimes passed through Context.
For example:
const AuthContext = createContext(null);
A provider might expose:
const login = useCallback(async credentials => {// ...},[]);
If a provider creates a new function on every render, consumers may observe changing context values.
However, remember that Context performance involves the identity of the complete context value too.
For example:
const value = useMemo(() => ({user,login}),[user, login]);
This is an advanced optimization pattern and should be used when context re-render behavior actually matters.
useCallback Does Not Make Functions Faster
This is an important misconception.
useCallback does not optimize the execution speed of:
handleClick();
The function itself does not become computationally faster.
Instead, useCallback can preserve its reference.
Think:
Function execution speed≠Function reference stability
useCallback addresses the second.
useCallback Does Not Prevent State Updates
Consider:
const handleClick = useCallback(() => {setCount(previous => previous + 1);}, []);
Clicking still updates state and causes the component to render.
useCallback does not block state updates.
It only controls whether the callback reference is recreated.
useCallback Is Not a Replacement for memo
These solve different problems:
useCallback↓Stable function referencememo↓Potentially skip child render
For callback optimization to affect a child, the child usually needs to care about prop identity, commonly through memo.
useCallback and Closures
A callback created during rendering closes over values from that render.
For example:
function User({ name }) {const greet = useCallback(() => {console.log(name);}, [name]);// ...}
The callback uses:
name
from the render that created it.
When name changes, the dependency changes and React can provide a new callback that closes over the new value.
This is why dependencies matter.
Stale Closures
Incorrect:
function User({ name }) {const greet = useCallback(() => {console.log(name);}, []);return <button onClick={greet}>Greet</button>;}
If name changes, the callback can retain the old value because it was memoized without name as a dependency.
Prefer:
const greet = useCallback(() => {console.log(name);}, [name]);
This keeps the callback synchronized with the value it reads.
Dependency Arrays Should Be Honest
Do not remove dependencies merely to keep a callback stable.
Incorrect thinking:
"I don't want this function to change,so I'll use []"
Correct thinking:
"What reactive values does this function read?"
Then specify those dependencies.
If the dependency list becomes inconvenient, reconsider the callback's design.
useCallback and Objects
Suppose:
const handleSave = useCallback(() => {save({name,});}, [name, email]);
This is appropriate if the callback needs the current values.
Do not try to force:
[]
just to keep the function stable.
Correctness comes first.
useCallback and Performance
The existence of useCallback does not mean every function should be memoized.
Memoization has its own costs and complexity.
For many components:
function Button() {const handleClick = () => {console.log("Clicked");};return <button onClick={handleClick}>Click</button>;}
is perfectly good.
Adding:
const handleClick = useCallback(() => console.log("Clicked"),[]);
may not improve anything.
When useCallback Is Useful
Good candidates include:
1. Callback passed to a memoized child
const handleDelete = useCallback(id => deleteTodo(id),[]);
2. Function used as a dependency
When a callback genuinely needs stable identity:
useEffect(() => {subscribe(handleEvent);return () => {unsubscribe(handleEvent);};}, [handleEvent]);
3. Custom Hook API
When a Hook returns callbacks whose stable identity benefits consumers:
return {increment: useCallback(...),reset: useCallback(...)};
4. Expensive child trees
When stable callback props help memoized child components avoid expensive rendering.
When useCallback Is Not Useful
Avoid it for:
Simple local handlers
function handleClick() {console.log("Clicked");}
Functions that are not passed anywhere important
function calculate() {return 10 + 20;}
Functions passed to ordinary DOM elements
<button onClick={handleClick}>Click</button>
A browser event handler does not normally need a stable React-level reference for optimization.
Every function by default
Do not adopt:
useCallback everywhere
as a coding standard without a reason.
Common useCallback Mistakes
Mistake 1: Using useCallback Everywhere
Avoid:
const handleClick = useCallback(() => console.log("Clicked"),[]);
when no consumer benefits from the stable identity.
Mistake 2: Thinking useCallback Caches Results
Incorrect:
const total = useCallback(() => calculateTotal(items),[items]);
This gives you a function, not the calculated total.
Use:
const total = useMemo(() => calculateTotal(items),[items]);
Mistake 3: Missing Dependencies
Incorrect:
const handleSave = useCallback(() => {save(user);}, []);
If user is reactive, it belongs in the dependency list:
const handleSave = useCallback(() => {save(user);}, [user]);
Mistake 4: Using useCallback to Hide an Architectural Problem
If a component is rendering too often, blindly memoizing every callback may hide the actual problem.
Investigate:
- State placement
- Component boundaries
- Context usage
- Expensive calculations
- Large lists
- Unnecessary parent renders
Mistake 5: Forgetting memoized Children
This:
const handleClick = useCallback(...);
does not automatically stop a child from rendering.
The child may need:
const Child = memo(...);
for stable callback props to contribute to render skipping.
Mistake 6: Optimizing Before Measuring
Do not assume that every new function is a performance problem.
Use profiling and real application behavior to identify actual bottlenecks.
Practical Example: Todo Application
import { memo, useCallback, useState } from "react";const TodoItem = memo(function TodoItem({todo,onDelete}) {console.log("Rendering:", todo.id);return (<li>{todo.text}<button onClick={() => onDelete(todo.id)}>Delete</button></li>);});function TodoList() {const [todos, setTodos] = useState([{ id: 1, text: "Learn React" },{ id: 2, text: "Learn Hooks" }]);const [search, setSearch] = useState("");const handleDelete = useCallback((id) => {setTodos(previousTodos =>previousTodos.filter(todo => todo.id !== id));}, []);const visibleTodos = todos.filter(todo =>todo.text.toLowerCase().includes(search.toLowerCase()));return (<div><inputvalue={search}onChange={event =>setSearch(event.target.value)}placeholder="Search"/><ul>{visibleTodos.map(todo => (<TodoItemkey={todo.id}todo={todo}onDelete={handleDelete}/>))}</ul></div>);}export default TodoList;
Here:
handleDelete↓useCallback↓stable function reference↓TodoItem receives same onDelete reference↓memo can optimize rendering
The list filtering itself is not memoized in this example because whether it needs memoization depends on the size and cost of the actual data.
Practical Example: Callback With State
function Counter() {const [count, setCount] = useState(0);const increment = useCallback(() => {setCount(previous => previous + 1);}, []);return (<button onClick={increment}>Count: {count}</button>);}
The callback has no dependency on count because it uses:
previous => previous + 1
instead.
This demonstrates a useful combination:
useCallback+functional state updater↓stable callback
Practical Example: Search Callback
function Search({ onSearch }) {const [query, setQuery] = useState("");const handleSearch = useCallback(() => {onSearch(query);}, [onSearch, query]);return (<div><inputvalue={query}onChange={event =>setQuery(event.target.value)}/><button onClick={handleSearch}>Search</button></div>);}
The callback depends on:
onSearchquery
Therefore:
[onSearch, query]
is the correct dependency list.
Practical Example: Custom Hook
import { useCallback, useState } from "react";function useCounter(initialValue = 0) {const [count, setCount] = useState(initialValue);const increment = useCallback(() => {setCount(previous => previous + 1);}, []);const decrement = useCallback(() => {setCount(previous => previous - 1);}, []);const reset = useCallback(() => {setCount(initialValue);}, [initialValue]);return {count,increment,decrement,reset};}
Usage:
function Counter() {const {count,increment,decrement,reset} = useCounter(10);return (<div><p>{count}</p><button onClick={increment}>+</button><button onClick={decrement}>-</button><button onClick={reset}>Reset</button></div>);}
This demonstrates how useCallback can be part of a reusable Hook API.
Debugging useCallback
If a callback changes unexpectedly, check:
1. Dependencies
useCallback(fn, [dependency])
Does one of those dependencies change?
2. Object dependencies
If:
const options = {};
is recreated every render, a callback depending on it can also change.
3. Parent rendering
A callback may be recreated because its dependencies changed due to state or props.
4. Is the callback actually causing a problem?
A changing function reference is not automatically a bug.
5. Is the child memoized?
If the goal is child render optimization, verify that the child uses an appropriate memoization strategy.
A Practical Optimization Checklist
Before adding useCallback, ask:
Is this function passed to a memoized child?│┌─────┴─────┐│ │Yes No│ │▼ ▼Could stable Usuallyidentity reduce no needchild renders?
Also ask:
Is this function used as a Hook dependency?│Yes↓Would stable identity improve synchronization?│Yes↓ConsideruseCallback
And finally:
Is there a measured performance problem?│No↓Avoid unnecessary memoization
useCallback and Component Design
Sometimes the best optimization is not memoization.
For example, instead of:
Large Parent↓Everything re-renders↓Many useCallbacks↓Many memoized children
a better architecture may be:
Parent├── Frequently changing component└── Stable component
Good component boundaries can naturally reduce unnecessary work.
Use memoization as a targeted optimization rather than as a substitute for component design.
useCallback and Referential Stability
Referential stability means keeping the same reference between renders when the relevant dependencies have not changed.
For a callback:
const handleClick = useCallback(() => doSomething(),[]);
the reference can remain stable.
This matters when code compares:
previousCallback === currentCallback
or when React's memoization mechanisms use reference equality.
The important distinction is:
Same behavior≠Same reference
useCallback is about the second.
useCallback and Memory
Memoization is not free.
React needs to retain the callback and track its dependencies.
Therefore:
No performance problem↓useCallback everywhere↓More complexity↓Potentially little or no benefit
Prefer targeted optimization.
Best Practices
1. Use useCallback for a Reason
A stable callback should solve an actual identity or performance problem.
2. Keep Dependencies Accurate
Do not remove dependencies just to preserve function identity.
3. Use Functional State Updates When Appropriate
This can reduce callback dependencies:
setCount(previous => previous + 1);
4. Combine With memoized Children When Appropriate
A stable callback is most useful for child optimization when the child can actually skip renders.
5. Do Not Confuse Function Identity With Execution Speed
useCallback does not make the function itself faster.
6. Prefer Simpler Code
If a normal function works well:
const handleClick = () => {};
do not add useCallback without a reason.
7. Profile Performance
Use React DevTools Profiler and browser tools when investigating actual rendering bottlenecks.
Interview questions & answers
Interview preparation
10 questions grouped by difficulty — expand any card for a model answer and follow-up probe.
Beginner
1What is useCallback?
BeginnerModel answer
useCallbackis a React Hook that caches a function definition between renders until its dependencies change.2What is the difference between useMemo and useCallback?
BeginnerModel answer
useMemocaches a calculated value.useCallbackcaches a function reference.3Why are dependencies important?
BeginnerModel answer
They ensure the callback sees current reactive values rather than stale values.
4Is useCallback always necessary for event handlers?
BeginnerModel answer
No.
Most event handlers do not need memoization.
Use it when stable function identity provides a concrete benefit.
Intermediate
5Why use useCallback?
IntermediateModel answer
It can provide a stable function reference, which can be useful for memoized child components, Hook dependencies, custom Hook APIs, and certain integrations.
6Does useCallback prevent re-renders?
IntermediateModel answer
No.
It does not prevent the component that owns the callback from rendering.
It can help a memoized child skip rendering when the callback prop remains referentially equal.
7Can useCallback be used without React.memo?
IntermediateModel answer
Yes, but its performance benefit is often limited unless another part of the system benefits from stable function identity.
Advanced
8Does useCallback make a function execute faster?
AdvancedModel answer
No.
It preserves the function reference; it does not optimize the function's internal execution.
9What happens when a dependency changes?
AdvancedModel answer
React can provide a new callback function reflecting the new dependency values.
10What is a stale closure?
AdvancedModel answer
A stale closure occurs when a function retains outdated values from an earlier render because the function was not correctly synchronized with the values it reads.
Practice Exercises
Exercise 1: Memoized Child
Create:
Parent↓Memoized Child
Pass a callback from the parent.
First implement it without useCallback.
Then add useCallback and observe the child's render behavior.
Exercise 2: Counter Callback
Create a counter where:
- The increment callback uses
useCallback. - The callback uses a functional state updater.
- The dependency array remains empty.
Explain why count does not need to be a dependency.
Exercise 3: Todo List
Create:
- Todo list
- Memoized Todo item
- Delete callback
- Search state
Use useCallback for the delete handler.
Observe which components render when the search term changes.
Exercise 4: Callback Dependencies
Create a callback that uses:
userIdtheme
Determine the correct dependency array.
Exercise 5: Stale Closure
Create an example where a callback incorrectly omits a dependency.
Observe the stale value.
Then fix it by adding the correct dependency.
Exercise 6: Remove Unnecessary useCallback
Given:
const handleClick = useCallback(() => {console.log("Clicked");}, []);
Determine whether the memoization provides a meaningful benefit.
If not, replace it with a normal function.
Mini Project: Optimized Todo Manager
Build a Todo Manager with:
Add TodoDelete TodoSearch TodoToggle completedTheme toggle
Use:
useStateuseCallbackmemo
Suggested architecture:
TodoManager│├── Search├── ThemeToggle└── TodoList│├── TodoItem├── TodoItem└── TodoItem
Requirements:
- Store todos with
useState. - Store search with
useState. - Create stable delete callback with
useCallback. - Create stable toggle callback with
useCallback. - Memoize
TodoItemwhere appropriate. - Use functional state updates.
- Observe render behavior with console logging.
- Avoid memoizing callbacks that provide no meaningful benefit.
Example:
const TodoItem = memo(function TodoItem({todo,onDelete,onToggle}) {return (<li><inputtype="checkbox"checked={todo.completed}onChange={() => onToggle(todo.id)}/><span>{todo.text}</span><button onClick={() => onDelete(todo.id)}>Delete</button></li>);});function TodoManager() {const [todos, setTodos] = useState([]);const [search, setSearch] = useState("");const handleDelete = useCallback((id) => {setTodos(previousTodos =>previousTodos.filter(todo => todo.id !== id));}, []);const handleToggle = useCallback((id) => {setTodos(previousTodos =>previousTodos.map(todo =>todo.id === id? {...todo,completed: !todo.completed}: todo));}, []);const visibleTodos = todos.filter(todo =>todo.text.toLowerCase().includes(search.toLowerCase()));return (<div><inputvalue={search}onChange={event =>setSearch(event.target.value)}placeholder="Search todos"/><ul>{visibleTodos.map(todo => (<TodoItemkey={todo.id}todo={todo}onDelete={handleDelete}onToggle={handleToggle}/>))}</ul></div>);}
The important optimization relationship is:
useCallback↓Stable callbacks↓memoized TodoItem↓Potentially fewer child renders
Quick Cheat Sheet
Import
import { useCallback } from "react";
Basic callback
const handleClick = useCallback(() => {console.log("Clicked");},[]);
Callback with dependency
const handleEdit = useCallback(() => {editUser(userId);},[userId]);
Functional state update
const increment = useCallback(() => {setCount(previous => previous + 1);}, []);
With memoized child
const Child = memo(function Child({onClick}) {return <button onClick={onClick}>Click</button>;});
Core distinction
useMemo→ memoize a valueuseCallback→ memoize a function referencememo→ potentially skip component rendering
Remember
useCallback does NOT:✗ cache function results✗ make function execution faster✗ prevent parent renders✗ make every callback necessaryuseCallback CAN:✓ preserve function identity✓ help memoized children✓ stabilize Hook dependencies✓ support custom Hook APIs