In our React development, we wrote code recklessly. At the beginning of the project, there might be no big problem, but as the project got bigger and more functions, the problems gradually became obvious. In order to make the project run normally and stably, we should think more about how to divide the code and how to write it when we write the code.

Here are some things to learn about react optimization:

Use react. PureComponent and react. memo

The react. PureComponent is similar to the React. Memo in that it reduces component re-rendering and improves performance under certain conditions.

React.PureComponent

React.purecomponent is very similar to react.component.the difference is that react.componentDoesn’t implement shouldComponentUpdate(), ShouldComponentUpdate () is implemented in the React.pureComponent as a shallow contrast between prop and state, so that when new props or state are introduced, The react. PureComponent will compare the new props and state to the original, and the component will not rerender if no changes are made.

Note that the react. PureComponent is only a superficial comparison. If the props or state has complex structures such as arrays or objects, this can be error-prone, requiring you to call forceUpdate() when the underlying data structure changes to ensure that the component is properly updated. You can also consider using immutable objects to speed comparison of nested data. If we write a custom shouldComponentUpdate() in react.pureComponent, the react.pureComponent will do away with the default shallow contrast and use a custom shouldComponentUpdate().

class RegularChildComponent extends Component<IProps.IState> {
    render() {
        console.log("Regular Component Rendered.."); // Every parent update is rendered
        return <div>{this.props.name}</div>; }}class PureChildComponent extends PureComponent<IProps.IState> {

  readonly state: Readonly<IState> = {
    age: "18",
  }

  updateState = (a)= > {
    setInterval((a)= > {
      this.setState({
        age: "18"})},1000)
  }

  componentDidMount() {
    this.updateState();
  }

  render() {
    console.log("Pure Component Rendered..")  // Only render once
    return <div>{this.props.name}</div>; }}class Demo extends Component<IProps.IState> {
  readonly state: Readonly<IState> = {
    name: "liu",
  }

  componentDidMount() {
    this.updateState();
  }

  updateState = (a)= > {
    setInterval((a)= > {
      this.setState({
        name: "liu"})},1000)
  }

  render() {
    console.log("Render Called Again") // Every component update is rendered
    return (
      <div>
        <RegularChildComponent name={'this.state.name'} />
        <PureChildComponent name={this.state.name} />
      </div>
    )
  }
}
Copy the code

React.memo(component, areEqual)

React.memo and react. PureComponent function is similar, but react. memo works with function components, not class components.

If your function component is rendering the same results given the same props, you can improve the performance of the component by wrapping it in a react.Memo call to remember the results of the component’s rendering. This means that in this case React will skip the render component and simply reuse the results of the last render.

By default, only shallow comparisons are performed on complex objects. If you want to control the comparison process, pass in your custom comparison function as a second argument.

// The parent component is the same as above
function ChildComponent(props: IProps){
  console.log("Pure Component Rendered..")  // Only render once
  return <div>{props.name}</div>;
}
const PureChildComponent = React.memo(ChildComponent);
Copy the code

Use opportunely shouldComponentUpdate (nextProps nextState)

In this function, we can decide whether to re-render the component and return false to tell React to skip the update. This method is not called the first time you render or use forceUpdate().

Note that deep comparisons or using json.stringify () in shouldComponentUpdate() are not recommended. This is very inefficient and can hurt performance. Returning false does not prevent child components from rerendering when state changes.

class Demo extends Component<IProps.IState> {
  readonly state: Readonly<IState> = {
    name: "liu".age: 18,
  }

  componentDidMount() {
    this.setState({
      name: "liu".age: 19,
    })
  }

  shouldComponentUpdate(nextProps: IProps, nextState: IState){
    if(this.state.name ! == nextState.name){return true;
    }
    return false;
  }

  render() {
    console.log("Render Called Again") // Prints only once
    return (
      <div>
        {this.state.name}
      </div>)}}Copy the code

In the example above, because the component only renders state.name, we don’t need to re-render when age changes but name doesn’t, so we can prevent rendering in shouldComponentUpdate(). In the example above, it is ok to block rendering, but if the current component or child component uses state.age, then we cannot block rendering based on name alone, otherwise we will have data and interface inconsistencies.

Therefore, when using shouldComponentUpdate() we should always know when we can prevent re-rendering.

Position of bind

When we create functions in React, we need to use the bind keyword to bind the function to the current context. The binding can be done in the constructor or at the point where we bind the function to the DOM element.

But when we bind the function to the location of the DOM element, we bind it every time we render, which causes some unnecessary performance loss and may result in unnecessary rendering of child components. So we can bind in the constructor, or we can write the arrow function directly. Similarly, we try not to write inline functions and attributes.

// good
class Binding extends React.Component<IProps.IState> {
  constructor() {
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    alert("Button Clicked")
  }
  
  render() {
    return (
      <input type="button" value="Click" onClick={this.handleClick} />
    )
  }
}
// good
class Binding extends React.Component<IProps, IState> {
  handleClick = () => {
    alert("Button Clicked")
  }
  
  render() {
    return (
      <input type="button" value="Click" onClick={this.handleClick} />
    )
  }
}
// bad
class Binding extends React.Component<IProps, IState> {
  handleClick() {
    alert("Button Clicked")
  }
  
  render() {
    return (
      <input type="button" value="Click" onClick={this.handleClick.bind(this)} />
    )
  }
}
Copy the code

Use the key

Key helps React identify which elements have changed, such as being added or removed. So you should give each element in the array a definite identity. Because of the existence of key, React diff algorithm has a qualitative leap.

An element’s key should ideally be a unique string that the element has in the list. Typically, we use the ID in the data as the element’s key. If the order of list items may change, we do not recommend using an index as a key value, as this can lead to poor performance and possibly component state issues.

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);
Copy the code

The code segment

When we have two or more “large components” in an interface that are mutually exclusive or unlikely to occur at the same time, we can split those components to reduce the size of the main JS.

// This component is dynamically loaded const OneComponent = react.lazy (() => import('./OneComponent')); const OtherComponent = React.lazy(() => import('./OtherComponent')); function MyComponent() { let isIOS = getCurrentEnv(); Suspense fallback={<Spinner />}> <div> {isIOS?  <OneComponent /> : <OtherComponent />> } </div> </React.Suspense> ); }Copy the code

In the code example above, OneComponent and OtherComponent are two large components. We render OneComponent on ios and OtherComponent on non-ios. Normally, only one of the two components will be loaded, so we can split up the code to ensure that the main JS does not include js that are not needed.

In most cases, we also split the code along route boundaries to speed up the first load of the interface.