React Advice for Faster Rendering



This is some advice that I’ve discovered through working on various React projects. Some of these practices are in response to issues with these projects. They are not intended to be a standard, but more of a guide – mainly for myself.

Don’t use componentWillReceiveProps

componentWillReceiveProps will be removed in React 17. It’s also often abused as some developers use it to set state.

Don’t set state in componentWillReceiveProps

Within the the lifecycle method order of React, componentWillReceiveProps comes before shouldComponentUpdate. If someone tried to set the state within componentWillReceiveProps then the shouldComponentUpdate function would fire once again. If the component didn’t use shouldComponentUpdate (because it’s not always necessary), then the render function would fire twice.

Use shouldComponentUpdate sparingly, but intelligently

React already performs a props and state comparison before running the render function, but it does so with a deepEquals test. What this means is that React is super fast with simple primitive props, such as strings, numbers, and booleans, but it can become slow with large objects.

There are several ways to speed up the default render check, but if you’re unable to make large structural changes in the codebase, then the simplest method is adding a shouldComponentUpdate and manually testing the props.

For some objects, it’s not a simple matter of testing if nextProp.myProp === this.prop.myProp, because the references might have changed even though the data is the same, so instead try JSON.stringify(nextProp.myProp) === JSON.stringify(this.prop.myProp). This comes with additional downsides, but is often faster than a full re-render.

Don’t use shouldComponentUpdate, use Containers

Wrapping a component in a container is a great was to directly access the datastore without requiring props to be passed by parent components. Instead of a waterfall of props, where the parent component needs to predetermine all of the props a child component would ever need, and passing them through a large list of JSX attributes, you can use the container to directly access the necessary information and reduce the amount of “knowledge” your parent components require.

Use selectors from reselect

Instead of adding data mutators within your component, or mutating your data when you retrieve it from the server creating duplicate data in the datastore, it’s easier to create a selector that will mutate the data for any component that wants to use it. In essence a selector could be a filter, or a way to change the datastore from one structure to a different one. Instead of an object of keys, maybe you need an array of entries.

Imagine the datastore holds an object referencing articles.

{
  "data": {
    "articles": {
      "abc": { ... },
      "def": { ... },
      "ghi": { ... }
    }
  }
}

Now, what if you need an array of articles for a list? Some people would pre-populate the datastore with the array once the AJAX service call is completed.

{
  "data": {
    "articles": {
      "abc": { ... },
      "def": { ... },
      "ghi": { ... }
    },
    "articlesArray": [{ ... }, { ... }, { ... }]
  }
}

This effectively doubles your datastore size and isn’t very efficient.

The other approach might be to add a filter in your components. This is another bad practice because if you add a filter to your component, maybe in the componentDidUpdate function, then the code could be duplicated in another component, adding to testability issues and code duplication.

The better approach is to create a selector which uses the data from the datastore to return the data in the format you require, in this case an array. Perhaps in another case you would need it sorted. You can always add options to the selectors to cover multiple cases.

Use Immutable.js

Immutable.js works by storing your data in the datastore as a class instead of a raw object. Some people find this frustrating because it’s more difficult to debug your data, but if you know about toJS() then this argument goes away.

Immutable also copies your data as properties of the component, so React can now use reference equality rather than deepEquals. A change to the data will only fire the render function once.

Use more basic props

Instead of passing an object or array, instead try passing a simpler ID or string-based property (could be a string or number for example). This way React’s default testing method doesn’t need to perform a deepEquals.

This last bit of advice builds upon using Containers, Selectors, and Immutable.js. Instead of passing large objects you can now use a container to use the simple ID in a selector to retrieve the immutable object from the datastore.

Leave a Reply

Your email address will not be published. Required fields are marked *