React Memo & Performance Optimization
Performance optimization is crucial for building fast and efficient React applications. React provides several built-in tools such as React.memo, useMemo, useCallback, and virtualization techniques to minimize unnecessary re-renders and improve performance.
Understanding React Re-Renders
React automatically re-renders components when:
✅ Their state or props change.
✅ Their parent component re-renders (even if props remain the same).
✅ A context value they consume updates.
Unnecessary re-renders can lead to performance issues, especially in large applications.
Preventing Unnecessary Re-Renders with React.memo
React.memo is a higher-order component (HOC) that optimizes functional components by preventing them from re-rendering unless their props change.
🎯 How React.memo Works
✔ If the props remain the same, React skips re-rendering.
✔ Improves performance for components that receive the same props repeatedly.
👉 Example: Using React.memo for Optimization
📌 Greeting.js
import React from ‘react’;
const Greeting = React.memo(({ name }) => {
console.log(“Greeting component rendered”);
return <h1>Hello, {name}!</h1>;
});
export default Greeting;
📌 Using React.memo in an App (App.js)
import React from ‘react’;
import Greeting from ‘./Greeting’;
function App() {
const [count, setCount] = React.useState(0);
return (
<div>
<Greeting name=”John” />
<button onClick={() => setCount(count + 1)}>Increment {count}</button>
</div>
);
}
export default App;
What Happens in This Example?
✔️ The Greeting component is wrapped with React.memo, so it only re-renders if the name prop changes.
✔️ Clicking the Increment button changes the count state.
✔️ Since name=”John” is not changing, the Greeting component does NOT re-render, reducing unnecessary work.
🎯 Why Use React.memo?
✔ Boosts performance by preventing unnecessary re-renders.
✔ Useful for components that don’t change often.
✔ Reduces wasted rendering cycles in large applications.
Optimizing Expensive Calculations with useMemo
useMemo caches the result of an expensive function and recalculates it only when dependencies change.
👉 Example: Using useMemo for Performance Improvement
import React, { useState, useMemo } from ‘react’;
function ExpensiveComponent({ number }) {
// Function to compute factorial (a costly operation)
const computeFactorial = (num) => {
console.log(“Computing factorial…”);
return num <= 1 ? 1 : num * computeFactorial(num – 1);
};
// useMemo caches the computed factorial until ‘number’ changes
const factorial = useMemo(() => computeFactorial(number), [number]);
return <h1>Factorial of {number}: {factorial}</h1>;
}
function App() {
const [number, setNumber] = useState(5);
return (
<div>
<input
type=”number”
value={number}
onChange={(e) => setNumber(Number(e.target.value))}
/>
<ExpensiveComponent number={number} />
</div>
);
}
export default App;
📝 How useMemo Works in This Example?
✔️ computeFactorial(number) is an expensive function.
✔️ useMemo(() => computeFactorial(number), [number])
• Caches the factorial value.
• Recomputes only when number changes.
✔️ Without useMemo, the function would run on every render, even if number remains the same.
🎯 Why Use useMemo?
✔ Prevents unnecessary re-execution of expensive functions.
✔ Enhances performance in computationally heavy tasks.
✔ Ideal for applications with complex calculations or large datasets.
Optimizing Functions with useCallback
useCallback memoizes functions, ensuring they are not re-created on every render.
👉 Example: Preventing Unnecessary Function Recreation
import React, { useState, useCallback } from ‘react’;
// Child component using React.memo
const Button = React.memo(({ handleClick }) => {
console.log(“Button rendered”);
return <button onClick={handleClick}>Click me</button>;
});
function App() {
const [count, setCount] = useState(0);
// Using useCallback to prevent function recreation
const increment = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return (
<div>
<h1>Count: {count}</h1>
<Button handleClick={increment} />
</div>
);
}
export default App;
📝 How useCallback Works in This Example?
✔️ The Button component is wrapped in React.memo, so it only re-renders if its props change.
✔️ Without useCallback, the increment function would be re-created on every render, causing Button to re-render unnecessarily.
✔️ With useCallback, increment is memoized and stays the same between renders, preventing unnecessary re-renders of Button.
🎯 Why Use useCallback?
✔ Prevents performance issues in large applications.
✔ Works well with React.memo() to avoid unnecessary child re-renders.
✔ Improves efficiency when passing event handlers to lists, forms, and components that don’t need to update frequently.
Virtualizing Long Lists with react-window
When rendering a large number of items in React, performance issues arise due to excessive DOM nodes. Virtualization helps by rendering only the visible portion of the list, reducing memory usage and improving performance.
👉 Installation:
npm install react-window
👉 Example: Implementing Virtualized Lists
import React from ‘react’;
import { FixedSizeList } from ‘react-window’;
// Row component for each list item
const Row = ({ index, style }) => (
<div style={style}>Item {index + 1}</div>
);
function App() {
return (
<FixedSizeList
height={200} // Visible height
width={300} // Width of the list
itemSize={30} // Height of each row
itemCount={1000} // Total number of items
>
{Row}
</FixedSizeList>
);
}
export default App;
📝 How Does Virtualization Work Here?
✔️ Only renders visible items – If 1000 items exist but only 7 fit in the viewport, it renders just those 7, not all 1000.
✔️ Reuses DOM elements – As you scroll, items are recycled instead of recreated.
✔️ Fixed size optimization – FixedSizeList assumes each item is the same height, optimizing rendering.
🎯 When to Use Virtualized Lists?
✔ When handling large datasets (e.g., product lists, user tables, chat messages).
✔ When scrolling causes UI lag due to too many elements in the DOM.
✔ When optimizing performance in data-heavy applications.
🎯 Benefits of Virtualization:
✔Instead of rendering 1000 items, React only renders visible items, reducing memory usage and improving performance.
Best Practices for Optimizing Performance
✅ Keep state minimal – Store only necessary state.
✅ Use local state instead of global state – Avoid unnecessary updates.
✅ Batch state updates – React batches multiple state updates for efficiency.
✅ Use React Developer Tools – Analyze and debug performance bottlenecks.
✅ Memoize expensive calculations – Use useMemo and useCallback.
✅ Virtualize long lists – Use react-window or similar libraries.
✅ Avoid unnecessary re-renders – Use React.memo for optimization.
Comparing React Optimization Techniques
Optimization Technique | Purpose |
---|---|
React.memo | Prevents re-renders when props don’t change |
useMemo | Caches expensive calculations |
useCallback | Memoizes functions to avoid recreation |
Virtualization (react-window) | Renders only visible list items |
Conclusion
Performance optimization in React is essential for building fast and efficient applications. Using tools like React.memo, useMemo, useCallback, and virtualization techniques ensures that your application runs smoothly and prevents unnecessary computations.