如何避免 React Component 不必要的重新渲染

How to avoid unnecessary re-rendering of React Component

我一直在学习 React 16.8 的新特性。我相信 React 的 Pure Component 应该会自动避免不必要的重新渲染操作。

在下面的例子中,App本身就是一个无状态组件。我使用 useState 维护两个状态对象 textnested: {text}.

有3个测试。前 2 个测试有效。无论我改变多少次状态,都不需要重新渲染操作。

现在,第三个测试尝试使用相同的字符串值设置 text 的状态,但引用不同。我不希望重新渲染任何内容,但实际上 <Headline/> 将被重新渲染。

我是不是要用一些死记硬背的技巧来避免?我觉得归档它的代码太多了。程序员必须非常小心才能编写高质量的 React 代码。 ..

class Headline extends React.PureComponent {
  render() {
   const {text} = this.props;
   return <h1>{text} (render time: {Date.now()})</h1>;
  }
} 

const simpleText = 'hello world'

const App = () => {
  const [text, setText] = React.useState(simpleText)
  const [nested, setNested] = React.useState({text: simpleText})
  return (
    <div>
      <Headline text={text}/>
      <Headline text={nested.text}/>

      <button onClick={()=>setText(simpleText)}>
        test 1: the first line should not change (expected)
      </button>

      <button onClick={()=>setNested({text: simpleText})}>
        test 2: the second line will not change  (expected)
      </button>

      <button onClick={()=>setText(new String(simpleText))}>
        test 3: the first line will change on every click (why?)
      </button>
    </div>
  )
}

ReactDOM.render(<App />, document.querySelector("#app"))

这是 jsfiddle 中的现场游乐场:

https://jsfiddle.net/fL0psxwo/1/

谢谢 React 朋友们,干杯!


更新 1: 感谢丹尼斯提到 why-did-you-render

作者点了一些很有用的文章。我认为这对每个人都非常有教育意义。 https://medium.com/welldone-software/why-did-you-render-mr-big-pure-react-component-part-2-common-fixing-scenarios-667bfdec2e0f

更新 2: 我创建了一个名为 withDirtyCheck 的新挂钩,以便我的代码自动执行内容脏检查。

import isEqual from 'lodash-es/isEqual';

export const withDirtyCheck = ([getter, setter]) => {
  const setStateIfDirty = (nextState) =>
    setter((prevState) => (isEqual(prevState, nextState) ? prevState : nextState));

  return [getter, setStateIfDirty];
};

查看我最新的图书馆 https://github.com/stanleyxu2005/react-einfach

问题是 new operator 你创建的 String Object 总是与之前的状态不同。

'hello world' === new String('hello world') // false, always.
'hello world' === String('hello world')     // true

检查这个 example:

setText(prevState => {

  // Will render
  // const currState = new String(simpleText);

  // Won't render
  const currState = String(simpleText);

  console.log(prevState === currState); // if true, no re-render
                                        // if false, re-render

  return currState;
});

参考What is the difference between string primitives and String objects in JavaScript?