Context是React项目中跨层级数据传递的API,当然这个API本身消耗很大。假如你有一个AppContext
,但是随着项目的复杂,appContextValue
变得越来越大,这个时候只要appContextValue
中有一个子值发生了改变,则涉及到的子组件都要更新,这对整个项目的性能是极其不利的。这个时候怎么办呢?
方案1(推荐):拆分无关联性的context
如下面的例子,点击两个button中的任何一个,都会导致ThemePage
与UserPage
的重新渲染,这不太合理,因为ThemePage
与appContextValue.user
无关,UserPage
与appContextValue.theme
无关。
export default function App(props) {
const [appContextValue, setAppContextValue] = useState({
theme: "red",
user: "小明",
});
let {themem, user} = appContextValue;
return (
<div>
<button
onClick={() => {
setAppContextValue({
...appContextValue,
theme: getRandomColor(),
});
}}>
点击修改主题色
</button>
<button
onClick={() => {
setAppContextValue({
...appContextValue,
user: getRandomName(),
});
}}>
点击修改用户姓名
</button>
<AppContext.Provider value={appContextValue}>
<ThemePage />
<UserPage />
</AppContext.Provider>
</div>
);
}
是时候做个拆分了,如下:
<AppContext.Provider value={appContextValue}>
<UserPage />
</AppContext.Provider>
<ThemeContext.Provider value={themeValue}>
<ExpensiveTree />
</ThemeContext.Provider>
方案2:使用React.memo
如果你因为某些原因不方便使用方案1拆分context,可以考虑使用React.memo:
<AppContext.Provider value={appContextValue}>
<ThemePage />
<UserPage user={user} />
</AppContext.Provider>;
这个时候的UserPage改为:
const UserPage = memo(({user}) => {
console.log("UserPage"); //sy-log 可以通过这个log检查组件是否发生不必要的更新
return (
<div className="border">
<h3>UserPage</h3>
<p>{user}</p>
</div>
);
});
方案3:使用React.useMemo
React.useMemo是一个hook函数,它的执行会返回一个 memoized 值,只有某个依赖项发生变化的时候才重新计算memoized值。原理上则是把这个值记录到了虚拟dom节点上。那么我们就可以使用useMemo记录这个组件,只有依赖项发生改变,才去重新更新组件。
<AppContext.Provider value={appContextValue}>
<ThemePage />
{useMemo(
() => (
<UserPage user={user} />
),
[user]
)}
</AppContext.Provider>;
这个时候的UserPage:
const UserPage = ({user}) => {
console.log("UserPage"); //sy-log 可以通过这个log检查组件是否发生不必要的更新
return (
<div className="border">
<h3>UserPage</h3>
<p>{user}</p>
</div>
);
};
以上三种方案可以用于解决appContextValue
过大导致的性能问题,当然,方案1是最理想的,毕竟解决的是根本问题,而方案2和方案3相对曲折了那么一点。最后,再提醒大家一句,Context好用,但是不要滥用哦!
最后,为什么写这个总结呢,因为看到了这个issue,正好最近做项目我的Context value也挺大的,正在寻找解决方案,看到这个文章,虽然我不一定会用这里的方案,但还是值得记录下的。