← Back to Chapters

useCallback()

⚛️ useCallback()

? Quick Overview

The useCallback() Hook memoizes functions in React so the same function reference is reused between renders unless dependencies change.

? Key Concepts

  • Memoizes functions (not values)
  • Prevents unnecessary child re-renders
  • Commonly used with React.memo()
  • Controlled by dependency array

? Syntax / Theory

? View Code Example
// Basic useCallback syntax
const memoizedCallback = useCallback(() => {
console.log("Callback executed");
}, [dependency]);

? Example 1: Without useCallback

? View Code Example
// New function created on every render
function Parent() {
const [count, setCount] = React.useState(0);

const handleClick = () => {
console.log("Clicked");
};

return (
<div>
<Child onClick={handleClick} />
<button onClick={() => setCount(count + 1)}>
Increment ({count})
</button>
</div>
);
}

const Child = React.memo(({ onClick }) => {
console.log("Child rendered");
return <button onClick={onClick}>Child</button>;
});

? Example 2: Optimized with useCallback

? View Code Example
// Stable function reference using useCallback
function Parent() {
const [count, setCount] = React.useState(0);

const handleClick = React.useCallback(() => {
console.log("Clicked");
}, []);

return (
<div>
<Child onClick={handleClick} />
<button onClick={() => setCount(count + 1)}>
Increment ({count})
</button>
</div>
);
}

const Child = React.memo(({ onClick }) => {
console.log("Child rendered");
return <button onClick={onClick}>Child</button>;
});

⚙️ Example 3: useCallback with Dependencies

? View Code Example
// Function recreated only when dependency changes
function Counter() {
const [count, setCount] = React.useState(0);
const [step, setStep] = React.useState(1);

const increment = React.useCallback(() => {
setCount(c => c + step);
}, [step]);

return (
<div>
<h4>Count: {count}</h4>
<button onClick={increment}>+{step}</button>
<input
type="number"
value={step}
onChange={e => setStep(Number(e.target.value))}
/>
</div>
);
}

? Example 4: useCallback + React.memo

? View Code Example
// useCallback + React.memo optimization
const List = React.memo(({ items, onSelect }) => {
return (
<ul>
{items.map(item => (
<li key={item} onClick={() => onSelect(item)}>
{item}
</li>
))}
</ul>
);
});

function App() {
const [selected, setSelected] = React.useState(null);
const items = ["Apple","Banana","Cherry"];

const handleSelect = React.useCallback(item => {
setSelected(item);
}, []);

return (
<div>
<List items={items} onSelect={handleSelect} />
<p>Selected: {selected || "None"}</p>
</div>
);
}

? Interactive Example (Conceptual Demo)

Click the buttons to see output below

Result will appear here…
? View Interactive Logic
// Simulating function recreation vs memoization (UI output)
let memoizedFn;
let recreateCount = 0;

document.getElementById("recreateBtn").addEventListener("click", () => {
recreateCount++;
const newFn = () => "New function created";
document.getElementById("demoOutput").textContent =
"Recreated Function Count: " + recreateCount + " → " + newFn();
});

document.getElementById("memoBtn").addEventListener("click", () => {
if (!memoizedFn) {
memoizedFn = () => "Same memoized function reused";
}
document.getElementById("demoOutput").textContent =
"Memoized Function → " + memoizedFn();
});

? Tips & Best Practices

  • Use only when passing callbacks to memoized children
  • Always declare correct dependencies
  • Avoid unnecessary memoization

? Try It Yourself

  1. Build a parent-child counter using useCallback
  2. Remove it and observe re-renders
  3. Add dependencies and test behavior