如何避免在无状态组件中使用基于 React class 的组件作为父组件进行不必要的更新

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

我正在学习 React 并且我发现 React.memo() "is not working",因为我的组件再次重​​新呈现我在父 class 上所做的每次更新零件。但问题是组件上的道具不会改变,至少对我来说是有意义的

我使用 useEffect 钩子在重新渲染的屏幕上打印,尽管我使用 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>
          );
        }
      }

我希望在我的控制台中,useEffect 挂钩中的消息 'rendered' 只出现一次。

React 钩子也有一个依赖数组,没有它 useEffect 钩子将在每次渲染时触发它的效果。 react hooks reference

useEffect(() => {
  console.log("rendered");
});  // every render

useEffect(() => {
  console.log("rendered");
}, []);  // on mount, once

useEffect(() => {
  console.log("rendered");
}, [propValue]);  // every time propValue is a new reference

memo HOC 函数还可以采用相等比较函数来进一步测试何时应该重新渲染,但请注意,React 仍然控制它们何时发生。 HOC(高阶组件)包装一个组件和 return 一个要渲染的新组件。您包装了您的组件,但不保存 returned 值以供稍后呈现。

const MemoizedComponent = React.memo(Component, [optional prop compare function]);
...
render() {
  return (
    <MemoizedComponent />
  );
};

发生这种情况是因为您使用 memo 的方式 - 您需要使用 React.memo(Men) 给您的 return 值。

像这样:

This CodePen will cause a re-render

This CodePen will NOT cause a re-render

正确:

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);

不正确:

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);