写点什么

React 中 useContext 和 useMemo 一般性使用

用户头像
sadhu
关注
发布于: 2021 年 03 月 14 日

通常,需要从某组件传递状态给深层嵌套的后代组件时,context 是一种可选方案。场景如应用“黑暗模式”主题等。


以下是一个类似的简单 demo,简单说下主要 3 个组件的关系,根组件(App)下有个工具栏(Toolbar/ToolbarExpensive)组件,工具栏组件中有个按钮(IButton/IButtonDefaultContextValue/IButtonExpensive)组件会随着主题变化而改变外观。


App 组件中,各个主要子组件一览

以上代码,我们看到有 context 的 Provider,有各种 Toolbar 组件,有各种 IButton 组件,Toolbar 外的 IButtonDefaultContextValue 组件用于体现 context 的默认值,我们可以点击各种 button 让不同的 IButton 组件外观发生变化。


当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。即使祖先使用 React.memo 或 shouldComponentUpdate,也会在组件本身使用 useContext 时重新渲染。


调用了 useContext 的组件总会在 context 值变化时重新渲染。如果重渲染组件的开销较大,你可以通过使用 memoization 来优化。


鉴于以上官方文档的说法,接下来我们看看如何使用 useMemo 来优化昂贵渲染开销的 IButtonExpensive 组件。


// ToobaarExpensive 组件import {useContext, useMemo} from 'react'
import IButtonExpensive from './ibutton-expensive'
import {Theme} from '../context/theme'
function ToobarExpensive() { const theme = useContext(Theme) return useMemo( () => ( <div> <p>带有 ibutton expensive 的 toolbar,利用 useMemo 优化渲染开销</p> <IButtonExpensive theme={theme} /> </div> ), [theme] )}
export default ToobarExpensive
复制代码


// IButtonExpensive 组件function IButtonExpensive(props) {  console.log(`render ibutton expensive, ${new Date()}`)  return <button style={{ background: props.theme }}>ibutton expensive</button>}
export default IButtonExpensive
复制代码


接着我们看看界面和按钮操作


demo 界面

首先我们点击 App 组件中的 change ibutton exprensive theme 按钮,Theme.Provider 更新(其 value 发生变化),App 组件内所有用到 useContext hook 的组件统统触发重渲染。我们注意看组件 IButtonExpensive,其被包裹在 useMemo 中,其父组件 ToobarExpensive 中有 useContext hook,触发重渲染时,useMemo 观察到 theme 有变化,IButtonExpensive 组件就要重渲染了。我们再点击 App 组件中 change ibutton theme 按钮,Theme.Provider 更新,照理说 App 组件内所有用到 useContext hook 的组件统统触发重渲染,但是组件 IButtonExpensive 在 ToobarExpensive 组件中的被 useMemo 包裹,如果 theme 没有变化,那么 IButtonExpensive 组件就不会重渲染了。


以下是 ToobarExpensive 组件和 IButtonExpensive 组件的另一种写法:

// ToobarExpensive 组件import IButtonExpensive from './ibutton-expensive'
function ToobarExpensive() { return ( <div> <p>带有 ibutton expensive 的 toolbar,利用 useMemo 优化渲染开销</p> <IButtonExpensive /> </div> )}
export default ToobarExpensive
// IButtonExpensive 组件import {useContext, useMemo} from 'react'import {Theme} from '../context/theme'
function IButtonExpensive() { console.log(`render ibutton expensive, ${new Date()}`) const theme = useContext(Theme)
return useMemo( () => <button style={{ background: theme }}>ibutton expensive</button>, [theme] )}
export default IButtonExpensive
复制代码

以上的写法即使有 useMemo,但是并不能阻止 IButtonExpensive 重渲染,因为如官方文档所说“调用了 useContext 的组件总会在 context 值变化时重新渲染。”


OK!就到这里。不周之处,请不吝拍砖!

源码地址:https://gitlab.com/a-sad-lab/rp

发布于: 2021 年 03 月 14 日阅读数: 15
用户头像

sadhu

关注

一个废废的老年悲观者 2013.05.14 加入

在下老菜鸟,github - https://github.com/sad-hu

评论

发布
暂无评论
React 中 useContext 和 useMemo 一般性使用