React.PureComponent 在组件有孩子时不起作用?
Posted
技术标签:
【中文标题】React.PureComponent 在组件有孩子时不起作用?【英文标题】:React.PureComponent doesn't work when the Component has children? 【发布时间】:2018-06-14 21:50:05 【问题描述】:使用 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 秒记录一次“重新渲染”。似乎 children(<div />
) 是上面 App
组件的唯一道具并且永远不会改变,为什么 App 仍然被重新渲染?
注意:如果有任何混淆,问题是一样的,为什么上面 PureComponent 的 SCU(shouldComponentUpdate) 钩子返回 true,因为似乎没有任何道具发生变化?
【问题讨论】:
请问您为什么在render
内调用render
?当您执行ReactDOM.render(..)
时,您已经渲染了您的应用程序,然后您再次递归调用它。这不是导致您的应用被多次重新渲染吗?
@margaretkru 我故意为它触发重新渲染以测试上面 App 纯组件的 SCU。补充说明:)
【参考方案1】:
这里发生的事情是你实际上是在调用ReactDOM.render()
,页面(或应用程序,我想你这里有错字)组件将触发其render()
函数,无论使用Component
或PureComponent
。
PureComponent 可以帮助减少不必要的渲染的方式是当prop 发生变化时,PureComponent 会对this.props
和nextProps
做一个浅比较,以确定这个PureComponent 是否需要调用@ 987654327@.
我刚刚为你做了这个例子:
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>
注意这几点:
-
两个孩子都收到一个固定的道具(“固定值”字符串)
每 1 秒,父级
<App />
更改 value
状态,因此它重新渲染,导致其所有子级也重新渲染。
但是由于<PureChild />
是一个PureComponent,它对它的旧props和传入的新props做了一个浅层的比较,并注意到两个props都是"fixed value"
,因此它不会触发渲染!李>
如果您运行此代码并打开控制台,您将每 1 秒看到一次“rendering ImpureChild”,但“rendering PureChild”只会出现一次。
【讨论】:
添加了注释,造成混乱是我的坏事,这可能不是我的问题,但感谢您的努力!【参考方案2】: console.log(<div /> === <div />) // false
在<App />
的每次重新渲染中,React.createElement(div, null)
创建了一个新 React Element,因此this.props.children
将不同于nextProps.children
,尽管它们在JSX 中看起来相同.
实际上,真正的问题是props.children
的引用(如果是原始类型,则为其他值)每次父重新渲染时都会发生变化,并且 React.PureComponent 会通过引用来比较 props,以包含不可变性。
【讨论】:
【参考方案3】:现在根据ReactDOM
的文档
ReactDOM.render()
控制你传递的容器节点的内容 in. 任何现有的 DOM 元素在第一次调用时都会被替换。 之后的调用使用 React 的 DOM diffing 算法进行高效更新。
ReactDOM.render()
不修改容器节点(只修改 容器的孩子)。可以插入一个 组件到现有的 DOM 节点而不覆盖现有的 孩子们。
ReactDOM 从第二次开始,只需使用它在其他地方使用的差异算法更新 React 组件,因此它不是 ReactDOM,这会导致重新渲染。您可以通过在 App 组件中添加 componentWillMount
方法来验证这一点,并检查它是否只被调用一次
现在来到 PureComponent。文档指出
React.PureComponent’s
shouldComponentUpdate()
只是浅比较对象。如果这些包含复杂的数据结构,则可能会为更深层次的差异产生假阴性。仅当您希望拥有简单的 props 和 state 时才扩展 PureComponent
所以这里有一个问题,PureComponent 可能会返回假阴性以获得更深层次的差异。因此,当您尝试比较 this.props.children
和 nextProps.children
是否相等时,您会发现它返回 false
并因此再次触发重新渲染
检查这个CodeSandbox
【讨论】:
谢谢先详细解答!补充说明,我对纯组件部分感兴趣,您能不能详细说明一下 props.children 的最后一部分,为什么 this.props 和 nextProps 会发生变化?【参考方案4】:根据 React.PureComponent 的文档
1)。 PureComponent 实现 shouldComponentUpdate() 与浅 props 和状态比较,将检查页面是否需要重新渲染
2)。如果 props 或 state 中有复杂的对象,那么 PureComponent 会给出误报,必须运行强制更新
3)。父组件的变化不会更新子组件,所以 PureComponent 的子组件也应该是 PureComponent
【讨论】:
以上是关于React.PureComponent 在组件有孩子时不起作用?的主要内容,如果未能解决你的问题,请参考以下文章
重构 pure() 与 React.PureComponent