如何避免不必要地重新渲染React组件

时间:2019-07-17 14:46:15

标签: javascript reactjs react-hooks

我最近一直在学习React 16.8的新功能。这个问题可能很愚蠢。我天真地认为,React的Pure Component应该自动避免不必要的重新渲染操作。

在以下示例中,App本身是无状态组件。我使用useState来维护两个状态对象textnested: {text}

有3个测试。前两个测试有效。无论有多少次,我都会更改状态,无需进行任何重新渲染操作。

现在,第三个测试尝试使用相同的字符串值设置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/

谢谢大家的反应,欢呼!


更新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

1 个答案:

答案 0 :(得分:3)

问题在于,使用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;
});

Edit antd-starter

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

相关问题