Introduction
React JS is widely used for building fast and interactive user interfaces. As applications grow in size and complexity, performance optimization becomes essential. React provides several hooks to help developers optimize performance, and one of the most important among them is the useCallback Hook.
In this article, we will understand what useCallback is, why it is used, how it works, and when to use it with real-world examples.
What is useCallback in React?
The useCallback Hook is used to memoize functions in React.
In simple words:
useCallback returns a cached version of a function that only changes when its dependencies change.
This helps prevent unnecessary re-creation of functions on every render.
Why do we need useCallback?
In React, every time a component re-renders:
- All functions inside it are recreated again
- Even if the logic is same, new function reference is created
This becomes a problem when:
- You pass functions to child components
- You use React.memo for optimization
- You work with large applications
Problem Example:
Without useCallback, child components may re-render unnecessarily because function references keep changing.
Syntax of useCallback
const memoizedCallback = useCallback(() => {
// function logic
}, [dependencies]);
Parameters:
- Function – The function you want to memoize
- Dependency array – Function is recreated only when dependencies change
Simple Example of useCallback
import React, { useCallback, useState } from "react";
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log("Button clicked");
}, []);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>
Increase Count
</button>
<ChildButton onClick={handleClick} />
</div>
);
}
function ChildButton({ onClick }) {
console.log("Child rendered");
return <button onClick={onClick}>Click Me</button>;
}
export default App;
What is happening here?
- handleClick is wrapped inside useCallback
- It does NOT change on every render
- So ChildButton does NOT re-render unnecessarily (if optimized with React.memo)
useCallback with React.memo
To fully benefit from useCallback, we often use it with React.memo.
Example:
import React, { useCallback, useState, memo } from "react";
const Child = memo(({ onClick }) => {
console.log("Child rendered");
return <button onClick={onClick}>Child Button</button>;
});
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log("Clicked");
}, []);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>
Increase
</button>
<Child onClick={handleClick} />
</div>
);
}
export default App;
Why this works better?
- useCallback prevents function recreation
- React.memo prevents child re-render
- Together, they improve performance significantly
Difference Between useCallback and Normal Function
| Feature | Normal Function | useCallback |
|---|---|---|
| Function Creation | Every render | Cached between renders |
| Reference Change | Yes | No (if dependencies unchanged) |
| Performance | Lower in large apps | Optimized |
| Use Case | Simple apps | Optimized React apps |
When should you use useCallback?
You should use useCallback when:
1. Passing functions to child components
If child is optimized with React.memo.
2. Preventing unnecessary re-renders
When function reference changes cause re-rendering.
3. Handling large applications
Where performance matters.
Real-World Example: Input Handler Optimization
import React, { useCallback, useState, memo } from "react";
const Input = memo(({ onChange }) => {
console.log("Input rendered");
return <input onChange={onChange} placeholder="Type here..." />;
});
function App() {
const [text, setText] = useState("");
const handleChange = useCallback((e) => {
setText(e.target.value);
}, []);
return (
<div>
<h2>Text: {text}</h2>
<Input onChange={handleChange} />
</div>
);
}
export default App;
What happens here?
- Input component will NOT re-render unnecessarily
- Because function reference remains stable
useCallback vs useMemo
Many developers confuse these two hooks.| Hook | Purpose |
|---|---|
| useCallback | Memoizes functions |
| useMemo | Memoizes values |
Example difference:
const memoValue = useMemo(() => computeValue(a, b), [a, b]);
const memoFunction = useCallback(() => {
console.log("Hello");
}, []);
Common Mistakes with useCallback
❌ 1. Using everywhere
Not all functions need memoization.
❌ 2. Wrong dependency array
Missing dependencies can cause stale function behavior.
❌ 3. Over-optimization
Sometimes it adds unnecessary complexity.
Advantages of useCallback
✔ Prevents unnecessary function creation
✔ Improves performance in large apps
✔ Works well with React.memo
✔ Reduces child component re-renders
Disadvantages of useCallback
❌ Slight memory overhead
❌ Can make code complex
❌ Not needed for small applications
Best Practices
✔ Use only when needed
✔ Combine with React.memo
✔ Keep dependency arrays accurate
✔ Avoid premature optimization
useCallback Hook – Interview Questions
Q 1: What is useCallback?
Q 2: How is useCallback different from useMemo?
Q 3: When should useCallback be used?
Q 4: Does useCallback improve performance?
Q 5: What happens if dependencies change?
useCallback Hook – Objective Questions (MCQs)
Q1. What is the main purpose of the useCallback hook in React?
Q2. Which syntax correctly uses useCallback?
Q3. Why is useCallback useful in React?
Q4. What happens if you do not provide a dependency array to useCallback?
Q5. Which of the following is TRUE about useCallback?
Conclusion
The useCallback Hook in React JS is a powerful performance optimization tool that helps prevent unnecessary function re-creation. It is especially useful when passing functions to child components and working with large-scale applications.
However, it should not be overused. React already optimizes most cases, so use useCallback only when you notice performance issues or unnecessary re-renders.