本文只讨论函数式组件,因为React不再建议使用class component。
过渡期完成以后,函数式组件成为正统的React的开发模型,"component"就用来指代function component,而classes会使用“legacy class components”的方式来指代。未来不会出现“必须使用class组件才能实现”的React行为。
React组件什么时候重新渲染
热身练习:对于下面这个demo,你能够不看实际输出,预测点击每个button分别会产生的结果吗?哪几个组件会重新渲染?为什么是这个结果?
Codesandbox demo
通过查看console.log的输出,你能够知道哪几个组件实际re-render了。从这个例子可以分析出React的重渲染的规律:
(1). 当Parent re-render时,Child也会re-render,除非以下2种情况之一发生:
- 父组件渲染的JSX tree中,在某个位置使用的
<Child/>
对象(react element,即React.createElement()
返回的对象)与上次渲染时相同(在同一个位置使用同一个对象引用),这时Child组件不会re-render。 Child组件被React.memo装饰,并且传给Child组件的props与上次渲染相同。
- 这种情况可以理解为
React.memo
帮我们缓存了上次渲染时使用的<Child/>
jsx element对象。即
- 这种情况可以理解为
const Component = React.memo(({count}) => {
console.log('Component re-render')
return <div>Count: {count}</div>
})
等价于
const Component = ({count}) => {
return React.useMemo(() => {
console.log('Component re-render')
return <div>Count: {count}</div>
}, [count]) // deps数组包含所有Parent可能传入的props value
}
(2). 当Child的状态更新并且re-render的时候,Parent不会re-render,只有Child以及它的后裔组件会re-render。并且Child组件re-render的时候,收到的props与上次渲染相同。
(3). 如果通过setState设置的新state与当前state相同,则不会触发re-render。(bail out)
如何做性能优化
基于上面总结的React重渲染规律,我们可以推导出以下的性能优化方式:
- 使用React.memo装饰组件,使得它在props相同的时候不重新渲染(它下面的子树也不会重新渲染)。
使用React.usememo来缓存计算结果、或者jsx element。它有2个方面的作用:
- 一方面,能够避免重复计算。当依赖与上次相同的时候,计算函数不会被调用,而是直接取上次缓存的计算结果。
- 另一方面,可以用来避免子组件的re-render。前面已经说了,如果
<Child/>
这个jsx element对象与上次渲染是同一个对象,那么Child就不会被重新渲染(它的render函数不会被调用)。因此,Parent可以使用React.usememo来缓存上次渲染时使用的<Child/>
jsx element对象,避免Child组件重新渲染。
Parent主动优化,给Child传入相同的
props.someJSX
(Parent可以使用useMemo来避免Child的props值变化)。当Child组件将props.someJSX
渲染出来的时候,React会发现这个React element与上次渲染时相同,于是不会尝试diff这个React element下面的节点- 因此,如果Parent知道Child的
props.someJSX
是不需要更新的,可以传递一个缓存的值给Child,避免这颗子树的diff和更新。props.children
经常能够这样优化。
- 因此,如果Parent知道Child的
如果Parent根本就没有重新渲染,re-render的起点是Child,此时Child的所有props必定与上次渲染相同。这种情况下,如果Child组件将
props.someJSX
渲染出来,React会发现这个React element与上次渲染时相同,于是不会尝试diff这个React element下面的节点。props.children
经常能够满足这种条件。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。