React applications can sometimes suffer from performance bottlenecks when complex or computationally expensive components re-render more often than necessary. Optimization in React largely involves preventing these redundant re-renders.
Memoization is the core technique used in React to achieve this optimization. It is an optimization strategy that stores the result of an expensive function call (or component rendering) and returns the cached result when the same inputs occur again.
1. The Problem: Unnecessary Re-renders
A React component automatically re-renders whenever:
- Its State changes (using
setState). - Its Props change (passed down from the parent).
- Its Parent component re-renders (even if the child’s props haven’t visually changed).
The third point is the biggest performance trap. If a parent re-renders, all its children re-render by default, which is often inefficient.
2. Optimization for Components: React.memo
React.memo is a Higher-Order Component (HOC) used to memoize functional components. It tells React: “Only re-render this component if its props have actually changed.”
Execution Example: Using React.memo
We will create a component that renders a complex list and prevent it from re-rendering when the parent’s unrelated state changes.
A. ComplexList.js (The Memoized Component)
Create or open ComplexList.js:
import React from 'react';
// Simulate an expensive, time-consuming component
function ComplexList({ list }) {
// Console log shows when the component actually renders
console.log('Rendering ComplexList...');
return (
<div style={{ padding: '15px', border: '1px solid orange' }}>
<h3>Memoized List (Will only update if 'list' prop changes)</h3>
<ul>
{list.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
// 1. Wrap the component export with React.memo()
export default React.memo(ComplexList);
B. App.js (The Parent Component with Unrelated State)
Open App.js and use the memoized component:
import React, { useState } from 'react';
import ComplexList from './ComplexList'; // The memoized component
function App() {
const [clickCount, setClickCount] = useState(0); // State 1: Changes frequently
const [data] = useState(['Item A', 'Item B', 'Item C']); // State 2: Stable data prop
return (
<div className="MemoDemo" style={{ padding: '20px' }}>
<h1>Component Memoization Demo</h1>
{/* 1. Component that re-renders frequently */}
<p>Clicks (Parent State): {clickCount}</p>
<button onClick={() => setClickCount(clickCount + 1)}>
Force Parent Re-render
</button>
<hr />
{/* 2. The memoized child component */}
<ComplexList list={data} />
</div>
);
}
export default App;
- Local Execution: Run
npm startand open your browser’s console. - Observation: When you click the “Force Parent Re-render” button, the
clickCountupdates, but the message “Rendering ComplexList…” in the console does not appear again. This confirms thatReact.memoprevented the redundant render because thelistprop remained unchanged.
3. Optimization for Functions: useCallback
When using React.memo for component optimization, you often run into a related issue with functions passed as props:
- Problem: Every time the parent component re-renders, it recreates all its functions, giving them a new memory reference.
- Result: The child component, even if wrapped in
React.memo, sees a new prop (the function reference) and re-renders anyway!
The useCallback Hook solves this by memoizing the function itself. It ensures the function is only recreated if one of its dependencies changes.
Syntax:
const memoizedCallback = useCallback(
() => { /* function body */ },
[dependency1, dependency2] // Array of dependencies
);
4. Optimization for Values: useMemo
The useMemo Hook is used to memoize the result of an expensive calculation (not the function definition itself). It prevents the calculation from running on every render, instead running only when one of its dependencies changes.
Syntax:
const cachedResult = useMemo(
() => {
// Perform expensive calculation here
return result;
},
[dependency1, dependency2] // Array of dependencies
);
Summary of Memoization Tools
| Tool | What it Memoizes | Purpose |
React.memo | A Component (the output render) | Prevents the component from re-rendering if its props are the same. |
useCallback | A Function Definition | Prevents a function from being recreated on every render, preserving its reference. |
useMemo | A Value (the result of a calculation) | Prevents an expensive calculation from re-running on every render. |
