当Component有子节点时,React.PureComponent不起作用?

时间:2018-01-05 05:16:13

标签: performance reactjs components react-dom

使用PureComponent改善React中的渲染性似乎是一种常见的技术。但是,使用具有子项作为道具的PureComponent时似乎并非如此。

class App extends React.PureComponent {
  render() {
    console.log('re-render') 
    return <div>{this.props.children}</div>
  }
}

const render = () => {
  ReactDOM.render(
    <App>
      <div />
    </App>,
    document.getElementById('app')
  )
  setTimeout(render, 1000)
}

render()

结果是控制台每1秒继续记录“重新渲染”。看起来孩子们(<div />)是上面App组件的唯一支柱并且永远不会改变,为什么App仍然会被重新渲染?

注意:如果有任何混淆,问题与上面PureComponent的SCU(shouldComponentUpdate)钩子返回 true 的原因相同,因为没有道具似乎改变了?

4 个答案:

答案 0 :(得分:5)

这里发生的事情是你实际上正在调用ReactDOM.render(),Page(或App,我认为你这里有一个拼写错误)组件将触发其render()函数,无论使用{{1} }或Component

PureComponent可以帮助减少不必要的渲染的方式是当进行道具更改时,PureComponent将对PureComponentthis.props进行浅层比较以确定此PureComponent需要致电nextProps

我刚给你做了这个例子:

render()

注意以下几点:

  1. 两个孩子都收到固定道具(&#34;固定值&#34;字符串)
  2. 每1秒,父class App extends React.PureComponent { state = {value: 0} componentDidMount() { setInterval(() => { this.setState({value: Math.random()}) }, 1000) } render() { return ( <div> <PureChild value="fixed value"/> <ImpureChild value="fixed value"/> </div> ) } } class PureChild extends React.PureComponent { render() { console.log('rendering PureChild') return <div>{this.props.value}</div> } } class ImpureChild extends React.Component { render() { console.log('rendering ImpureChild') return <div>{this.props.value}</div> } } 更改<App />状态,因此会重新渲染,导致其所有子项重新渲染。
  3. 但是,因为value是一个PureComponent,它对旧道具和传入的新道具进行浅层比较,并且通知两个道具都是<PureChild />,因此它不会#39; t触发渲染!
  4. 如果您运行此代码并打开控制台,您只能看到“渲染ImpureChild”#39;每1秒,但是会呈现PureChild&#39;只会出现一次。

答案 1 :(得分:4)

 console.log(<div /> === <div />) // false

每次<App />的重新呈现时,React.createElement(div, null)都会创建 React Element,因此this.props.children将与nextProps.children不同虽然它们在JSX中看起来一样。

实际上,真正的问题是,props.children的引用(否则值是原始类型)在每次父渲染时都会发生变化,而React.PureComponent会通过引用来比较具有不变性的道具。

答案 2 :(得分:1)

现在根据ReactDOM

的文档
  

ReactDOM.render()控制您传递的容器节点的内容   in。第一次调用时,里面的任何现有DOM元素都会被替换。   后来的调用使用React的DOM diffing算法进行有效的更新。

     

ReactDOM.render()不会修改容器节点(仅修改   容器的孩子们)。可以插入一个   组件到现有DOM节点而不覆盖现有节点   孩子。

从第二次开始,ReactDOM只是使用它在其他地方使用的差异算法更新React组件,因此它不是ReactDOM,导致重新渲染。您可以通过在App Component中添加componentWillMount方法来验证这一点,并检查它是否只被调用一次

现在来到PureComponent。文档说明了

React.PureComponent’s shouldComponentUpdate()只是浅析对象。如果这些包含复杂的数据结构,则可能会产生更深层次差异的假阴性。只有在您希望拥有简单的道具和状态

时才能扩展PureComponent

所以这是捕获,PureComponent可能会返回错误的负面以获得更深层次的差异。因此,当您尝试将this.props.childrennextProps.children进行比较以获得相等性时,您会发现它返回false,因此会再次触发重新渲染

选中 CodeSandbox

答案 3 :(得分:0)

根据React.PureComponent的文档

1)。具有浅层属性和状态比较的PureComponent实现shouldComponentUpdate(),将检查是否需要重新渲染页面

2)。如果道具或状态中有复杂的对象,那么PureComponent将给出错误的肯定结果,必须运行强制更新

3)。更改父组件不会更新子组件,因此PureComponent的子组件也应该是PureComponent