How does react.purecomponent differ from react.component.react

React.PureComponent is very similar to react.component.react.component.react.pureComponent. The difference is that shouldComponentUpdate() is not implemented in react.pureComponent, which is implemented in a shallow way to compare prop and state.

I can sort out two points

  1. The React.PureComponent implements shouldComponentUpdate internally. If you give the React component the same props and state, the component will not rerender

  2. Both the old and new props/state are superficial comparisons

As the concept sounds shallow, this article uses a few small demos to actually test the differences between them and deepen the impression, while also adding a comparison with functional components. After all, functional components have taken over since React hooks came along

All Demo code is available here

Use the React Developer Tools to observe the React component render

1 Give the React component the same props and state

DEMO 1 has the same old and new props

In this Demo, we write two files, parent. Js and children.js. The former sets a timer that executes this.setState every second. Note that parentCount is 0 each time. ParentCount is then passed into three child components: React.PureComponet, React.component, and functional components.

// parent.js

import React from "react";
import logo from "./logo.svg";
import "./App.css";
import { ComponentChild, FunctionalChild, PureChild } from "./components/children";

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      parentCount: 0
    }
    this.interval = null;
  }
  componentDidMount() {
    this.interval = setInterval(() = > {
      this.setState({
        number: 0 // Set a value equal to the initial value})},1000)
  }
  componentWillUnmount () {
    clearInterval(this.interval)
  }
  render() {
    const { parentCount } = this.state
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          {/* React.PureComponent */}
          <PureChild parentCount={parentCount} />
          {/* React.Component */} 
          <ComponentChild parentCount={parentCount} />{/* Functional components */}<FunctionalChild parentCount={parentCount} />
        </header>
      </div>); }}export default Parent;
Copy the code
// children

import React, { Component, PureComponent } from "react";

class PureChild extends PureComponent {
  render() {
    console.log("Render PureChild components");
    return <span>{`Pure Child Component ${new Date().valueOf()}`}-{this.props.parentCount}</span>; }}class ComponentChild extends Component {
  render() {
    console.log("Render ComponentChild components");
    return <span>{`Child Component - ${new Date().valueOf()}`}-{this.props.parentCount}</span>; }}function FunctionalChild(props) {
  console.log("Render FunctionalChild components");
  return (
    <span>{`Functional Child Component - ${new Date().valueOf()}`}-{props.parentCount}</span>
  );
}
export {
  FunctionalChild,
  PureChild,
  ComponentChild
}
Copy the code

You can see that everything except the Pure Component is rerendered, regardless of whether the parentCount prop passed in is changed. Rerendering in this case is unnecessary and wastes performance.

For functional components, you can useReact.memoOptimize its performance

DEMO 2 has the same state as the old one

Change the code in DEMO 1 to move the timing setting of state into the child components and set the same value each time

// parent.js
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import { ComponentChild, FunctionalChild, PureChild } from "./components/children";

class Parent extends React.Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <PureChild />
          <ComponentChild />
          <FunctionalChild />
        </header>
      </div>); }}export default Parent;
Copy the code
// children.js
import React, { Component, PureComponent, useState, useEffect } from "react";

class PureChild extends PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      counter: 0
    }
    this.interval = null;
  }
  componentDidMount() {
    this.interval = setInterval(() = > {
      this.setState({
        counter: 0})},1000)}componentWillUnmount() {
    clearInterval(this.interval)
  }
  render() {
    console.log("Render PureChild components");
    return <span>{`Pure Child Component ${new Date().valueOf()}`}</span>; }}class ComponentChild extends Component {
  constructor(props) {
    super(props)
    this.state = {
      counter: 0
    }
    this.interval = null;
  }
  componentDidMount() {
    this.interval = setInterval(() = > {
      this.setState({
        counter: 0})},1000)}componentWillUnmount() {
    clearInterval(this.interval)
  }
  render() {
    console.log("Render ComponentChild components");
    return <span>{`Child Component - ${new Date().valueOf()}`}</span>; }}function FunctionalChild() {
  const [counter, setCounter] = useState(0);
  useEffect(() = > {
    const interval = setInterval(() = > {
      setCounter(0)},1000);
    return () = > clearInterval(interval)
  }, [])
  console.log("Render FunctionalChild components");
  return (
    <span>{`Functional Child Component - ${new Date().valueOf()}`}</span>
  );
}
export {
  FunctionalChild,
  PureChild,
  ComponentChild
}
Copy the code

It turns out that onlyReact.ComponentThe writing component has been updated. In fact, the functional component here is not updated becausehooksThe reason why. call State HookThe update function is passed to the currentstateWhen,ReactWill skip the rendering and of the child components effectThe execution. (use the ReactObject.isCompare algorithms to comparestate)

2 The old and new props/state are only superficial comparisons

DEMO 3 props Shallow comparison

Here we change parentCount in the parent component to an array parentArray, push a value every second, and print the length of the array in the child component

// parent.js

import React from "react";
import logo from "./logo.svg";
import "./App.css";
import { ComponentChild, FunctionalChild, PureChild, FunctionalChildMemo } from "./components/children-props-shallow-compare";

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      parentArray: [1.2.3.4]}this.interval = null;
  }
  componentDidMount() {
    this.interval = setInterval(() = > {
      const { parentArray } = this.state;
      this.setState({
        parentArray: [...parentArray, parentArray.length + 1]})},1000)}componentWillUnmount() {
    clearInterval(this.interval)
  }
  render() {
    const { parentArray } = this.state
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <PureChild parentArray={parentArray} />
          <ComponentChild parentArray={parentArray} />
          <FunctionalChild parentArray={parentArray} />
          <FunctionalChildMemo parentArray={parentArray} />
        </header>
      </div>); }}export default Parent;
Copy the code
// children.js
import React, { Component, PureComponent } from "react";

class PureChild extends PureComponent {
  render() {
    console.log("Render PureChild components");
    return <span>{`Pure Child Component ${this.props.parentArray.length}`}</span>; }}class ComponentChild extends Component {
  render() {
    console.log("Render ComponentChild components");
    return <span>{`Child Component - ${this.props.parentArray.length}`}</span>; }}function FunctionalChild(props) {
  console.log("Render FunctionalChild components");
  return (
    <span>{`Functional Child Component - ${props.parentArray.length}`}</span>
  );
}
function FunctionalChild2(props) {
  console.log("React.memo FunctionalChild component render");
  return (
    <span>{`React.memo Functional Child Component - ${props.parentArray.length}`}</span>
  );
}
const FunctionalChildMemo = React.memo(FunctionalChild2)
export {
  FunctionalChild,
  FunctionalChildMemo,
  PureChild,
  ComponentChild
}
Copy the code

You can see that only the Pure Component and the components wrapped in React. Memo are not updated. If you’re rendering an array as a list, you’re going to have a mismatch between the data and the view. Of course, this is just to verify the shallow comparison of the use of demo, the actual development should be similar to the following writing method, there will be no problems mentioned above

// ...
      const { parentArray } = this.state;
      
      this.setState({
        parentArray:[...parentArray, parentArray.length + 1]})// ...
Copy the code

DEMO 4 State Shallow comparison

This moves the array changes inside the child component

// parent.js
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import { ComponentChild, FunctionalChild, PureChild } from "./components/children-state-shallow-compare";

class Parent extends React.Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <PureChild />
          <ComponentChild />
          <FunctionalChild />
        </header>
      </div>); }}export default Parent;
Copy the code
// children js
import React, { Component, PureComponent, useState, useEffect } from "react";

class PureChild extends PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      array: [1.2.3.4]}}componentDidMount() {
    setInterval(() = > {
      const { array } = this.state;
      array.push(array.length + 1)
      this.setState({
        array
      })
    }, 1000)}render() {
    console.log("Render PureChild components");
    return <span>{`Pure Child Component ${this.state.array.length}`}</span>; }}class ComponentChild extends Component {
  constructor(props) {
    super(props)
    this.state = {
      array: [1.2.3.4]}}componentDidMount() {
    setInterval(() = > {
      const { array } = this.state;
      array.push(array.length + 1)
      this.setState({
        array
      })
    }, 1000)}render() {
    console.log("Render ComponentChild components");
    return <span>{`Child Component - ${this.state.array.length}`}</span>; }}function FunctionalChild(props) {
  const [array, setArray] = useState([1.2.3.4]);
  useEffect(() = > {
    setInterval(() = > {
      array.push(array.length + 1)
      setArray(array)
    }, 1000)}, [])console.log("Render FunctionalChild components");
  return (
    <span>{`Functional Child Component - ${array.length}`}</span>
  );
}
export {
  FunctionalChild,
  PureChild,
  ComponentChild
}
Copy the code

You can see there’s onlyReact.ComponentThe component is re-rendered. It is worth noting here that the functional component is also not updated, verifying that the update function of State Hook is usedObject.is()Also shallow comparison

reference

reactjs.org/

Blog.logrocket.com/react-pure-…

Medium.com/technofunne…