This is the 11th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

preface

When developing React, have you ever encountered a problem like “sometimes we just want to update a single component, but it triggers a lot of unrelated components to render?” Normally, we only want to rerender the components with changed data, and not the other unrelated components. So we need to avoid ineffective repeat renderings, because React coordination costs are expensive.

There are two types of performance issues in React, long lists and repeated renderings. A long list is when your page renders a very long list, usually hundreds, thousands, or even thousands of lines of data. Long lists per se are not a React framework specific problem, but can be encountered in any technology stack. The universal solution is virtual rolling and the best solutions in the industry are React-virtualized and React-window.

Is there a good solution to repeat rendering? Let’s take a look today.

1. Locate the gadget

React Developer Tools is a tool used in React. Use the Profiler in React Developer Tools to analyze component render times, start times, and time consuming. React Profiler use advice to read the official document in detail: zh-hans.reactjs.org/blog/2018/0…

If you already know how to use the React Developer Tools gadget, skip this section.

In the tool Settings, turn on “Highlight Updates when Components render.” When we build the render, it will be highlighted on the page.

Or you can develop the recording function, after the page is finished, will see the corresponding rendering, not rendering content, will be directly marked asDid not render. Repeat rendered content can be directly viewed rendering time and other messages.

React-dom 16.5+ supports performance analysis in DEV mode. The React-DOM/Profiling code package is also available for performance analysis in a production environment. Check out Fb. me/ React-Profiling to learn more about how to use this package.

2. A test example

The following code is a simple example code (sample code from the network code) that simply moves a table up and down,

  • List, a component that displays lists and performs up-down logic;
  • ListItem, the rows shown in the list, renders the contents of each row.

Codesandbox.io /s/test-rese…

import React from "react";
const initListData = [];

for (let i = 0; i < 10; i++) {
  initListData.push({ text: i, id: i });
}

const ListItem = ({ text, onMoveUp, onMoveDown, index }) = > (
  <div>
    <span>{text}</span>
    <button onClick={()= >OnMoveUp (index)} > up</button>
    <button onClick={()= >OnMoveDown (index)} > move down</button>
  </div>
);

class List extends React.Component {
  state = {
    listData: initListData
  };

  handleMoveUp = (index) = > {
    const _listData = [...this.state.listData];
    // Partial implementation
    if (index === 0) {
      const data = _listData.shift();
      _listData.push(data);
      this.setState({
        listData: _listData }); }}; handleMoveDown =(index) = > {
    const _listData = [...this.state.listData];
    // Partial implementation
    if (index === 0) {
      const data = _listData.pop();
      _listData.unshift(data);
      this.setState({
        listData: _listData }); }};render() {
    const { listData } = this.state;
    return (
      <div>
        {listData.map(({ text, id }, index) => (
          <ListItem
            text={text}
            index={index}
            onMoveUp={this.handleMoveUp}
            onMoveDown={this.handleMoveDown}
          />
        ))}
      </div>); }}export default function App() {
  return (
    <div className="App">
      <List />
    </div>
  );
}
Copy the code

When we move the list rows up and down, we find that when we operate on one row, the rest of the ListItem rows are also rerendered. As stated at the beginning of the article, we only want to rerender the components with changed data, and we don’t want to involve other unrelated components, because React coordination costs are expensive. So how to avoid unnecessary duplication of rendering, then read on.

3. React.memo

React.memo – added in Act 16.6 to optimize performance of Functional components: React.memo. React.memo() is a higher-order function similar to react.pureComponent, but a function component instead of a class.

Adding react. memo to ListItem prevents each line from being rerendered. The following code looks like this:

const ListItem =  React.memo(({ text, onMoveUp, onMoveDown, index }) = > (
  <div>
    <span>{text}</span>
    <button onClick={()= >OnMoveUp (index)} > up</button>
    <button onClick={()= >OnMoveDown (index)} > move down</button>
  </div>
));
Copy the code

Note that not every line will render when you add React. Memo. However, the react. memo method uses the props and state function to compare the props and state before and after the change. This method can easily fail. The arrow function dynamically generates a new function every time it is called render, and the reference to the function changes. This will not work even with react.memo, so be aware when using it. Authors often write with arrow functions, but this is a bad habit. A better way to write is to extract the entire function as a class attribute.

Of course, if you’re using React Hooks, you can use useMemo, which is called the more subtle memo. React. Memo controls rerendering at the component level. React. UseMemo controls rerendering of one or more parts of a component. Memo controls whether a component needs to be rerendered, while useMemo controls whether a piece of logic needs to be executed repeatedly.

4. PureComponent

PureComponent has a built-in implementation for shouldComponentUpdate: PureComponent will perform a shallow comparison of props and states before and after the component is updated in shouldComponentUpdate and determine whether to continue the update process based on the results. Also, PureComponent has the same urinability as React.memo, which is to use the arrow function.

class ListItem extends React.PureComponent{
  render() {
    const { text, onChange, index } = this.props;
    return (
      <div>
        <input value={text} />
        <span>{text}</span>
        <button onClick={()= >OnChange (index)} > modify text</button>
      </div>); }};Copy the code

ImmutableJS- immutable data

React. Memo, PureComponent, and PureRenderMixin all use shallow comparisons to compare props and states before and after changes. They compare value equality only for value type data, and only for reference types such as arrays, objects, and so on. So it’s not going to be very accurate.

  • If the content of the data has not changed, but the reference to the data has changed, the shallow comparison will still consider “the data changed” and trigger an unnecessary update, resulting in overrendering.
  • If it is deeply nested and the data content changes but the reference does not change, the shallow comparison will say “the data has not changed” and block an update, resulting in no rendering. ImmutableJS guarantees that the modification operation returns a new reference and only modifies the node that needs to be modified. Immutable structure immutability && Structure sharing enables rapid comparison of data:
function deepCompare(instance, nextProps, nextState) {
  return! Immutable.is(instance.props, nextProps) || ! Immutable.is(instance.state, nextState); }class ListItem extends React.Component{
  shouldComponentUpdate(nextProps, nextState) {
    return deepCompare(this, nextProps, nextState);
  };
  render() {
    const { text, onChange, index } = this.props;
    return (
      <div>
        <input value={text} />
        <span>{text}</span>
        <button onClick={()= >OnChange (index)} > modify text</button>
      </div>); }};Copy the code

Although ImmutableJS can solve repeated rendering in some cases, if ImmutableJS needs to interact with the server frequently, then ImmutableJS needs to be constantly converted to native JS, which is cumbersome to operate, and this scheme has a certain mental cost to some extent. So immerjs is more popular these days.

6. Reselect – cache

Reselect maps the input to the output and caches the results of the function. As long as the input is consistent, the corresponding output result will be spit out directly, so as to ensure that the calculation result is unchanged, in order to ensure that it will not be broken. This is done by caching, using resELECT to cache the results of the execution function to avoid creating new objects. Xiaobian I more than this program is not deep understanding, also have not used, but if you are interested in you can try it.

Making: github.com/garylesueur…

7. Manual control

Handle this manually by using the shouldComponentUpdate API, but shouldComponentUpdate can cause unexpected bugs, so this should be considered as a last resort. And this approach is limited to class components.

class ListItem extends React.Component{
  shouldComponentUpdate(nextProps, nextState) {
    if(nextProps.text === this.props.text) {
      return false
    }
    return true
  }
  render() {
    const { text, onChange, index } = this.props;
    return (
      <div>
        <input value={text} />
        <span>{text}</span>
        <button onClick={()= >OnChange (index)} > modify text</button>
      </div>); }};Copy the code

Before using

After using

conclusion

A common solution to avoid rerendering is to use PureComponent or use a component cache API such as React.memo (useMemo) to reduce rerendering. But using it in the wrong way, like using arrow functions or generating new objects every time, makes it completely useless. These problems can be solved by using ImmutableJS, immerJS to transform data structures, or resELECT to cache the results of function execution. Or their own manual control, their own implementation shouldComponentUpdate function, but this kind of program is generally not recommended, because it is easy to bring unexpected bugs, can be used as a guarantee.

Code words are not easy, if it helps you, help to click like it.

reference

  • Blog.csdn.net/Sunday97/ar…
  • Github.com/garylesueur…
  • Zhenhua – lee. Making. IO/react/Immut…
  • Kaiwu.lagou.com/course/cour…
  • www.npmjs.com/package/res…
  • Github.com/garylesueur…
  • Zh-hans.reactjs.org/blog/2018/0…