React组件是否会深度比较道具以检查是否需要重新渲染?

时间:2018-10-25 06:31:21

标签: javascript reactjs

我想知道扩展React.Component的React组件在尝试确定是否需要重新渲染时是否深深地比较了该对象。

例如,给定

const Foo = ({ bar }) => {
  return <div>{bar.baz}</div>
}

class App extends React.Component {
  constructor() {
    super()
    this.state = { bar: { baz: 1} }
  }

  render() {
    return <Foo bar={this.state.bar} />
  }
}

如果在App内,状态bar变为{baz: 2}<Foo />是否将前一个道具bar与新接收的道具进行深度比较?

偶然地,the docs for PureComponent

  

扩展PureComponent ...或者,考虑使用不可变对象来促进嵌套数据的快速比较。

但是没有涉及更多细节。有什么想法吗?

2 个答案:

答案 0 :(得分:5)

除非您自己实现shouldComponentUpdate生命周期方法,否则扩展 React.Component 的组件将不会在通过比较之前与之前的渲染周期之前比较道具。和当前的虚拟DOM决定要重新渲染的内容

使用 React.PureComponent 扩展的组件不会将props与prevProps进行深度比较,而state与prevState进行深度比较,而是进行浅层比较以确定是否需要重新渲染

对于 v16.6.0 起的 Functional component ,React引入了高阶函数React.memo,可用于制作一个功能组件的行为类似于React.PureComponent

根据 docs

  

React.memo是一个高阶组件。这类似于   React.PureComponent,但用于功能组件而不是类。

     

可以像

一样使用
const MyComponent = React.memo(function MyComponent(props) {
  /* render using props */
});
     

如果函数组件在相同条件下呈现相同结果   道具,您可以将其包装在对React.memo的调用中以提高性能   在某些情况下,通过记忆结果。这意味着React将跳过   渲染组件,然后重用最后渲染的结果。

     

默认情况下,道具只会shallowly compare complex objects   宾语。如果您想控制比较,还可以提供   自定义比较函数作为第二个参数。

答案 1 :(得分:0)

'Extend PureComponent'被引用在上下文之外。文档说的相反,

  

仅当您希望具有简单的道具和状态时才扩展PureComponent

因为

  

React.PureComponent的shouldComponentUpdate()仅浅浅地比较对象。如果这些数据包含复杂的数据结构,则可能会产生假阴性,从而产生更大的差异。

反应组件不会比较道具,除非它们实现shouldComponentUpdate,例如PureComponent。可以使用完全相同的道具({===相等)重新渲染组件。

如果状态发生了变化但没有被强制更新(这是一种反模式),则不会重新渲染子级。

如果状态发生变化并被强制更新(这是一种反模式),则会重新渲染子级:

class App extends React.Component {
  componentDidMount() {
     this.state.bar.baz = 2;
     this.setState(state);
  }
  ...    
}

为了将更新限制为变更较深的道具,Foo应该实施shouldComponentUpdate并进行深度比较,例如Lodash isEqual

class Foo extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return !_.isEqual(this.props, nextProps) || this.state !== nextState;
  }
  ...
}

由于深度比较的成本可能很高,因此应该进行性能测试以确定它是否可以提供性能改进。

不可改变的状态和道具在React中非常受欢迎,因为它们可以避免这个问题。如果某个对象以某种方式更改,则应将其替换为另一个对象,即状态更新应生成新的statestate.bar对象:

class App extends React.Component {
  componentDidMount() {
    this.setState(({ bar })=> ({
      bar: { ...bar, baz: 2 }
    });
  }
  ...    
}

在这种情况下,Foo需要浅比较它作为道具接收的bar对象,因此它可以是PureComponentReact.memo(如另一个答案所解释)。