This site runs best with JavaScript enabled.

The First And The Most Rewarding Step In Optimizing React App


React is usually fast, but there are situations when we need to make some optimizations to avoid performance issues.

Firstly, we need to understand in which cases React might get slow. When a component’s state changes, React has to calculate if it is necessary to update the DOM. It does this by comparing the old React elements to a new set of React elements. This process is called reconciliation. When there are a lot of elements to compare, like a big SVG file (here is an example), React provides us shouldComponentUpdate lifecycle method. This method allows your Component to exit the Update lifecycle if there is no reason to apply a new render.

1class OptimizedComponent extends Component {
2 shouldComponentUpdate(nextProps, nextState) {
3 // You can access `this props` and `this.state` here
4 // This function should return a boolean, whether the component should re-render.
5 return false
6 }
7 render() {
8 return // ...
9 }
10}

Another way of using shouldComponentUpdate is to use React.PureComponent. React.PureComponent is similar to React.Component. The difference between them is that React.Component doesn’t implement shouldComponentUpdate(), but React.PureComponent implements it with a shallow prop and state comparison.

If you have functional component and your app is using at least React v16.6.0, you can use React.memo, which is a higher order component. It’s similar to React.PureComponent but for function components instead of classes.

1const MyComponent = React.memo(function MyComponent(props) {
2 // render using props
3})

If you have functional component and don’t want to transform it into a class-based component you can use the shouldComponentUpdate logic in a higher-order component (HOC) provided by recompose. Recompose is a functional utility belt for React, providing the pure HOC, beside others.

1import React from 'react'
2import pure from 'recompose/pure'
3const HelloWorld = ({name}) => <div>{`Hi $${name}`}</div>
4export default pure(HelloWorld)

You can be even more specific and target only the props that you know may change, using recompose’s shouldUpdate instead of pure.

1import React from "react";
2import shouldUpdate from "recompose/shouldUpdate";
3const HelloWorld = ({ ids, data }) => (
4 // ...
5);
6
7const checkPropsChange = (props, nextProps) =>
8 nextProps.ids !== props.ids || nextProps.data !== props.data;
9export default shouldUpdate(checkPropsChange)(HelloWorld);

The recompose library offers more performance HOCs, like onlyUpdateForKeys, which does exactly the type of check from above checkPropsChange

1import React from "react";
2import onlyUpdateForKeys from "recompose/onlyUpdateForKeys";
3const HelloWorld = ({ ids, data }) => (
4 // ...
5);
6
7export default onlyUpdateForKeys(['ids','data'])(HelloWorld);

If you are using prop-types library, than you might try using onlyUpdateForPropTypes which uses onlyUpdateForKeys under the hood:

1const enhance = compose(
2 onlyUpdateForPropTypes,
3 setPropTypes({
4 /**
5 * The value of this cell
6 */
7 value: PropTypes.string,
8
9 /**
10 * Column headings render prop
11 */
12 render: PropTypes.func,
13 }),
14)
15
16export default enhance(Cell)

Recompose, beyond performance optimization, helps you extract data fetching logic, HOC composition, and props manipulation in a functional and testable way.

Notice: Use PureComponent and shouldComponentUpdate only when you need to, as described in the beginning of the article.

Vadim Nicolai

Vadim Nicolai is a JavaScript software engineer.