Search code examples
reactjsreact-hooksreact-lifecyclereact-lifecycle-hooks

How to avoid unnecesary update in a stateless component with a React class-based component as a parent


I´m learning React and i found that React.memo() "is not working", because my component again re-render on every update that i do on the parent class-based component. But the problem is that props on component don´t change, at least it make sense for me

I used useEffect hook to print on my screen that re-render, although i use React.memo(Men)

const Men = props => {
        useEffect(() => {
          console.log("rendered");
        });
        return <p onClick={props.add}>{props.adder}</p>;
    };
    React.memo(Men);

    class App extends React.Component {
        state = {
          counter: 0,
          adder: "press"
        };

        add = () => {
          this.setState(prevState => {
            return {
              counter: prevState.counter + 1
            };
          });
        };

        render() {
          return (
            <div className="App">
              <p>{this.state.counter}</p>
              <Men adder={this.state.adder} add={this.add} />
            </div>
          );
        }
      }

I expect that in my console the message 'rendered' inside the useEffect hook appears only once time.


Solution

  • This is happening due to how you are using memo - you need to use the return value that React.memo(Men) gives you.

    Like this:

    This CodePen will cause a re-render

    This CodePen will NOT cause a re-render

    Correct:

    const MenBefore = props => {
      React.useEffect(() => {
        console.log("rendered");
      });
      return <p onClick={props.add}>{props.adder}</p>;
    };
    ////////////////////////////////////////
    const Men = React.memo(MenBefore); // <--- THIS LINE
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    class App extends React.Component {
      state = {
        counter: 0,
        adder: "Click Me - I will -NOT- cause a re-render"
      };
    
      add = _ => {
        this.setState(prevState => {
          return {
            counter: prevState.counter + 1
          };
        });
      };
    
      render() {
        return (
          <div className="App">
            <p>{this.state.counter}</p>
            <Men adder={this.state.adder} add={this.add} />
          </div>
        );
      }
    }
    
    ReactDOM.render(<App />, document.body);
    

    Incorrect:

    const Men = props => {
      React.useEffect(() => {
        console.log("rendered");
      });
      return <p onClick={props.add}>{props.adder}</p>;
    };
    /////////////////////////////
    React.memo(Men); // <<<--------- WRONG
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    class App extends React.Component {
      state = {
        counter: 0,
        adder: "Click Me - I will cause a re-render"
      };
    
      add = _ => {
        this.setState(prevState => {
          return {
            counter: prevState.counter + 1
          };
        });
      };
    
      render() {
        return (
          <div className="App">
            <p>{this.state.counter}</p>
            <Men adder={this.state.adder} add={this.add} />
          </div>
        );
      }
    }
    
    ReactDOM.render(<App />, document.body);