渲染优化
useContext
我们先看一个传统的Context的渲染例子。
import React,{createContext,useContext,useState} from "react"
import { ColorBlock,Button,Box } from "x-react-components"
const ctx = createContext({
firstName:"Zhang",
lastName:"Fisher",
age:18
})
const Child = (props:any)=>{
const context=useContext(ctx)
return <ColorBlock name={`子组件:${props.name}`}>
<span>Hello :{context.firstName}{' '}{context.lastName}</span>
</ColorBlock>
}
let count:number = 0
export default ()=>{
const [user,setUser] = useState({
firstName:"Zhang",
lastName:"Fisher",
age:18
})
return <ctx.Provider value={user}>
<Box title="根组件">
<ColorBlock name={'FullName'}>{user.firstName}{' '}{user.lastName}</ColorBlock>
<ColorBlock name={'Age'}>Age :{user.age}</ColorBlock>
</Box>
<Box title="子组件">
<Child name="A"/>
<Child name="B"/>
</Box>
<Button onClick={()=>{
setUser({firstName:"Zhang",lastName:"Fisher",age:++count})
}}>+Age</Button>
</ctx.Provider>
}
从上面的例子可看到,当更新Context.age时,所有的子组件不管是否有使用Age均会重新渲染,而这是不必要的,因为子组件并没有使用到Context的数据,为此我们一般需要使用React.memo或一些第三方库来进行优化渲染。 此方案最大的问题在于,当更新根Context时,所有的子组件都会重新渲染,这是不必要的,因为子组件并没有使用到根Context的数据。我们希望能实现更细粒度的渲染,只有当子组件使用到的数据发生变化时,才会重新渲染
React.Memo
为了优化渲染逻辑,一般我们会使用`React.memo来进行优化渲染。
const Child = React.memo((props:any)=>{
const context=useContext(ctx)
return <ColorBlock name={`子组件:${props.name}`}>
<span>Hello :{context.firstName}{' '}{context.lastName}</span>
</ColorBlock>
},(prop,oldProp)=>{
return .....
}
- 经过优化后,当更新
Age时,仅根组件会重新渲染,而FirstName和LastName不会重新渲染,因为它们并没有使用到Age。 - 当在根组件中更新
FirstName时,仅FirstName会重新渲染。而LastName组件中没有FirstName,所以不会重新渲染。 在大型React应用,面对复杂的状态变化,如何决定何时使用React.memo是一个很大的心智问题,也是最容易搞坑里的,这也是为什么React官方要推Compiler的原因**
信号组件
而更好的办法就是最近比较流行的signal机制,signal机制可以将渲染颗粒度限定在组件范围,只有使用到数据的组件才会重新渲染。 基于Signal,渲染颗粒度可以是组件中的一个片段或ReactNode,更加精细,更加高效。 了解更多关于Signal的内容,可以阅读深入解析:React中的信号组件与细粒度更新