Welcome back to the React Mastery Series!
In the previous article, we explored useRef and useMemo Hooks and learned how React helps developers optimize applications by:
- Storing values without triggering re-renders
- Accessing DOM elements
- Memoizing expensive calculations
- Improving rendering performance
Today, we will explore two important performance optimization concepts:
useCallback and React.memo
These concepts become extremely important when building:
- Large enterprise applications
- Complex dashboards
- Data-heavy applications
- Component libraries
- High-performance React systems
Understanding React Re-rendering
Before learning optimization, we need to understand rendering behavior.
A React component re-renders when:
- State changes
- Props change
- Parent component re-renders
- Context value changes
Example:
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>Increase</button>
<Child />
</>
);
}
Every time count changes:
State Update
|
↓
Parent Re-renders
|
↓
Child Re-renders
Even though:
Child data did not change
React still renders it.
What is React.memo?
React.memo is a higher-order component that prevents unnecessary re-renders.
It tells React:
Re-render this component only when its props change.
Syntax:
const ComponentName =
React.memo(Component);
Example Without React.memo
Parent:
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increase</button>
<Child />
</div>
);
}
Child:
function Child() {
console.log("Child Render");
return <h2>Child Component</h2>;
}
Output:
Initial Load
Child Render
Click Button
Child Render
Child Render
The child renders every time the parent updates.
Optimizing with React.memo
Example:
const Child = React.memo(function Child() {
console.log("Child Render");
return <h2>Child Component</h2>;
});
Now:
Initial Load
Child Render
Parent State Changes
Child Does Not Render
because its props did not change.
Understanding Props Comparison
React.memo performs a shallow comparison.
Example:
<Child name="Siva" />
React compares:
Before:
{
name:"Siva"
}
After:
{
name:"Siva"
}
No change:
Skip Render
Problem with Objects as Props
Consider:
function Parent() {
const user = {
name: "Siva",
};
return <Child user={user} />;
}
Even though the data looks same:
{
name:"Siva"
}
A new object is created every render.
React sees:
Previous Object
≠
New Object
because references are different.
Result:
Child Re-renders
Combining React.memo with useMemo
Solution:
const user = useMemo(
() => ({
name: "Siva",
}),
[],
);
Now:
Object Reference Remains Same
|
↓
React.memo Works Correctly
|
↓
Child Avoids Re-render
Understanding useCallback
Now let's explore:
useCallback
useCallback is used to memorize functions.
Syntax:
const memoizedFunction = useCallback(
function,
dependencies
);
It returns the same function reference until dependencies change.
Why Do We Need useCallback?
Consider:
function Parent() {
const handleClick = () => {
console.log("Clicked");
};
return <Child onClick={handleClick} />;
}
Every render creates a new function:
Render 1
handleClick → Function A
Render 2
handleClick → Function B
Even though the logic is identical:
Function A ≠ Function B
React thinks props changed.
Using useCallback
Example:
function Parent() {
const handleClick = useCallback(() => {
console.log("Clicked");
}, []);
return <Child onClick={handleClick} />;
}
Now:
Render 1
handleClick → Function A
Render 2
handleClick → Function A
The reference remains the same.
Real Example with React.memo and useCallback
Parent:
function TodoApp() {
const [todos, setTodos] = useState([]);
const addTodo = useCallback(() => {
setTodos((prev) => [...prev, "New Task"]);
}, []);
return <TodoButton onAdd={addTodo} />;
}
Child:
const TodoButton = React.memo(function TodoButton({ onAdd }) {
console.log("Button Render");
return <button onClick={onAdd}>Add Todo</button>;
});
Flow:
Parent Render
|
↓
Create Function
|
↓
useCallback Stores Function
|
↓
Child Receives Same Reference
|
↓
React.memo Prevents Render
useCallback vs useMemo
Many developers confuse these Hooks.
| Hook | Stores |
|---|---|
| useMemo | Computed values |
| useCallback | Functions |
Example:
useMemo
const total = useMemo(()=>{
return calculateTotal(items);
},[items]);
Stores:
Result Value
useCallback
const save = useCallback(()=>{
submitForm();
},[]);
Stores:
Function Reference
Real-World Example: Banking Application
Imagine a banking dashboard:
Components:
Dashboard
|
|
├── AccountSummary
|
├── TransactionList
|
└── TransferButton
User changes profile settings.
Without optimization:
Dashboard Re-render
|
↓
All Child Components Render
|
↓
Unnecessary Processing
With optimization:
Dashboard Re-render
|
↓
React.memo Checks Props
|
↓
Unchanged Components Skip Rendering
When Should You Use React.memo?
Good use cases:
✅ Large reusable components
✅ Data tables
✅ Charts
✅ Lists with many items
✅ Expensive UI rendering
Example:
Transaction Table
|
↓
1000 Rows
Memoization can significantly improve performance.
When Should You Avoid React.memo?
Do not use everywhere.
Example:
const SmallText = React.memo(()=>
<p>Hello</p>
);
For simple components:
- Comparison cost
- Extra complexity
may outweigh benefits.
Common Mistakes
Mistake 1: Using useCallback Everywhere
Avoid:
const click = useCallback(()=>{
console.log("hello");
},[]);
For simple functions, normal functions are enough.
Mistake 2: Forgetting Dependencies
Incorrect:
useCallback(()=>{
save(user);
},[]);
Problem:
user is missing.
Correct:
useCallback(()=>{
save(user);
},[user]);
Mistake 3: Expecting useCallback to Improve Everything
useCallback only helps when:
- Function references matter
- Child components use React.memo
- Dependencies are controlled
Performance Optimization Strategy
A practical approach:
Application Slow
|
↓
Measure Performance
|
↓
Find Expensive Components
|
↓
Optimize Specific Areas
|
↓
Use memoization Carefully
Do not optimize before identifying the problem.
React Developer Tools
React provides Developer Tools to analyze:
- Component renders
- Props changes
- Performance issues
Useful features:
- Profiler
- Render highlighting
- Component inspection
Best Practices
- Optimize only when needed.
- Use React.memo for expensive components.
- Use useCallback when passing functions to memoized children.
- Use useMemo for expensive calculations.
- Keep components small and focused.
- Measure before and after optimization.
Key Takeaways
Today, we learned:
✅ React.memo prevents unnecessary component renders.
✅ useCallback memoizes function references.
✅ useMemo stores calculated values.
✅ Memoization is useful for performance optimization.
✅ Optimization should be based on actual performance issues.
Coming Next 🚀
In Day 16, we will explore:
State Management in React – Understanding Context API
We will learn:
- Why global state is needed
- Problems with prop drilling
- Creating Context
- Provider pattern
- useContext Hook
- Real-world authentication example
State management is one of the most important topics for building scalable React applications.
Happy Coding! 🚀
Top comments (0)