Do we need PureComponent in Redux project?

I. What

I had such question when I first join my current company. We use react-redux lib, and let redux to manage nearly all data in frontend.

  1. [Fact 1]: We all know that the parent re-render will trigger children component re-render by default (if no shouldComponentUpdate provided in child component, it will always default to true). This way, it might introduce unnecessary re-render for children component even though it should NOT.
  2. [Fact 2]: Redux creating HoC Pure component for its corresponding UI components. That is, container components actually inherit PureComponent (by default, it implements shouldComponentUpdate and return false if props and state stay the same). Thus, there is no need we use pure components for the UI components, as its container components already handle it

  3. [Question]: Does that means we don’t need PureComponent in Redux project? ——>NO

II. Why?

  1. Actually, Redux uses connect() to create HoC Pure component, which actually inherits PureComponent (by default, it implements shouldComponentUpdate and return false if props and state stay the same). All UI components now hide behind the Container components, which are Pure.Components
  2. But, we cannot guarantee all the components have a corresponding container components created by redux. Still, there exist react component not plugging into redux store. In this case, it will trigger child component re-render if the parent component re-render
  3. So, we still need to inherit PureComponent in some case in redux project

III. Code

Play with part_1 and part_2, to see when:

  • child plugging into redux;

  • child not plugging into redux;

  • child not plugging into redux, but with shouldComponentUpdate()

  1. App.jsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import { Provider } from "react-redux";
    import React from "react";
    import ReactDOM from "react-dom";

    import Parent from "./Parent";
    import store from "./store";

    class App extends React.Component {
    render() {
    return (
    <Provider store={store}>
    <div className="App">
    <Parent />
    </div>
    </Provider>
    );
    }
    }

    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
  2. Parent.jsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import { connect } from "react-redux";
    import React from "react";
    import Child from "./Child";

    let cnt_parent = 0;
    class Parent extends React.Component {
    constructor() {
    super();
    this.state = {
    parentStr: "This is parent String"
    };
    }

    clickHandler = () => {
    this.setState({
    parentStr: "Parent String changed!"
    });
    };

    render() {
    console.log("parent cnt: ", cnt_parent++);
    return (
    <div
    onClick={() => {
    this.clickHandler();
    }}
    >
    {this.state.parentStr}
    <Child />
    </div>
    );
    }
    }

    export default Parent;
  3. Child.jsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import React from "react";
    import { connect } from "react-redux";
    let cnt_child = 0;

    class Child extends React.Component {
    static defaultProps = { childStr: "This is child String" };
    /*
    // Part_1
    shouldComponentUpdate() {
    return false;
    }
    */
    render() {
    console.log("child cnt: ", cnt_child++);
    return <div>{this.props.childStr}</div>;
    }
    }

    /*
    // Part_2
    const mapStateToProps = state => ({
    childStr: state.child
    });

    const mapDispatchToProps = dispatch => {};

    export default connect(
    mapStateToProps,
    null
    )(Child);
    */

    export default Child;