There are some times when re-rendering of the component results in performance issues. To overcome this, React provides us with a performance feature known as memoization.
Memoization is an optimization technique that allows an increase in the performance of a program by storing the results of some expensive function so that we don’t need to call that function when the same inputs are given.
React has the features PureComponent and memo hook which allow us to implement memoization in React.
PureComponent allows us to perform optimization. It depends on the shouldComponentUpdate() lifecycle method but it can only be used with the class component.
React also gives us a memo() hook to apply memoization for functional components.
If we need a function component that gives the same result for the same props and we don’t want to re-render it, we can use memoization to skip the re-render of the component by storing and reusing the last rendered result.
PureComponent is similar to Components in React except that PureComponent implements shouldComponentUpdate() with shallow prop and state comparison and Components does not.
In some cases, if our component re-renders the same result with the same given props and state which results in performance issues, then we can use PureComponent to increase performance. PureComponent’s shouldComponentUpdate() only shallowly compares the object.
But we should make sure that props and state are simple. If they contain any complex data structures then PureComponent may produce wrong results. Also, we should make sure that all the children components of PureComponent are also PureComponent since PureComponent’s shouldComponentUpdate() skips prop updates for the full component subtree.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React, {
Component,
PureComponent
} from 'react';
import './App.css';
import User from './User';
class App extends PureComponent {
constructor() {
super();
this.state = {
count: 10
}
}
render() {
console.warn('render');
return (
<div className="App">
<p>Count {this.state.count}</p>
<button onClick={()=>{this.setState({count:20})}}>Update</button>
</div>
);
}
}
export default App;
In React, the memo is the higher-order component in short HOC (HOC are functions that take a component and return a new component). Memo allows us to implement memoization in functional components since PureComponents can only be used in class components.
1
2
3
const MyComponent = React.memo(function MyComponent(props) {
/* render using props */
});
We can make a custom comparison function and pass it as a second argument to memo to control its default comparison behavior, which shallowly compares complex objects in the props object.
1
2
3
4
5
6
7
8
9
10
11
12
function MyComponent(props) {
/* render using props */
}
function areEqual(prevProps, nextProps) {
/*
if results are same when passing prevProps and when
passing nextProps then areEqual will return true, if
results are not same then it will return false
*/
}
export default React.memo(MyComponent, areEqual);
We can say that areEqual is the inverse of shouldComponentUpdate() since it returns true if props are equal and false if props are not equal.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const List = React.memo(({
items
}) => {
log('renderList');
return items.map((item, key) => (
<div key={key}>item: {item.text}</div>
));
});
export default function App() {
log('renderApp');
const [count, setCount] = useState(0);
const [items, setItems] = useState(getInitialItems(10));
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>
inc
</button>
<List items={items} />
</div>
);
}
If you are a Hook fan then you can use useMemo for implementing memoization. To utilize useMemo we need to pass a create function and an array of dependencies. useMemo optimizes performance by recomputing the memoized value only when one of the passed dependencies changes.
1
2
/* use of useMemo hook for memoization */
const valueMemoized = useMemo(() => computeExpensiveValue(a, b), [a, b]);
We should only implement useMemo for performance optimization and for that we should first write our code such that it works without useMemo and then add useMemo to optimize it. Also, we should know that useMemo runs during rendering so we need to make sure that we do not use anything that is not used during rendering, like side effects.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import {
useState,
useMemo
} from 'react';
export function CalculateFactorial() {
const [number, setNumber] = useState(1);
const [inc, setInc] = useState(0);
const factorial = useMemo(() => factorialOf(number), [number]);
const onChange = event => {
setNumber(Number(event.target.value));
};
const onClick = () => setInc(i => i + 1);
return (
<div>
<input type="number" value={number} onChange={onChange} />
= {factorial}
<button onClick={onClick}>Render again</button>
</div>
);
}
function factorialOf(n) {
console.log('factorialOf(n) called!');
return n <= 0 ? 1 : n * factorialOf(n - 1);
}
Memoization can increase performance for some functions. But, if we use it for every component rendering, it might decrease performance since it stores results that increase memory used for the program. We should only use memoization when there is a clear benefit for doing so.
Memoization is an optimization feature in React which, when used in the right place, increases the performance of the program. React gives us PureComponent and memo to implement memoization. PureComponent is used with the class component and memo is used with the function component. Memoization increases performance by storing results for functions when the same prop is passed, hence reducing the number of re-renderings. But, overuse of memoization in places where there are not performance issues can result in reduction of performance.