createContext 你用对了吗?
前言
createContext
是 react 提供的用于全局状态管理的一个 api,我们可以通过Provider
组件注入状态,用Consumer
组件或者useContext
api 获取状态(推荐使用useContext
方式,更加简洁)。
createContext
让组件间的通信更为方便,但如果使用不当却会带来很大的性能问题。下面我们会讨论引起性能问题的原因以及如何优化。
性能问题的根源
先来看一个例子:createContext性能问题原因,注意例子中的2个问题点。
import { useState, useContext, createContext } from "react";
import { useWhyDidYouUpdate } from "ahooks";
const ThemeCtx = createContext({});
export default function App() {
const [theme, setTheme] = useState("dark");
/**
* 性能问题原因:
* ThemeCtx.Provider 父组件渲染导致所有子组件跟着渲染
*/
return (
<div className="App">
<ThemeCtx.Provider value={{ theme, setTheme }}>
<ChangeButton />
<Theme />
<Other />
</ThemeCtx.Provider>
</div>
);
}
function Theme() {
const ctx = useContext(ThemeCtx);
const { theme } = ctx;
useWhyDidYouUpdate("Theme", ctx);
return <div>theme: {theme}</div>;
}
function ChangeButton() {
const ctx = useContext(ThemeCtx);
const { setTheme } = ctx;
useWhyDidYouUpdate("Change", ctx);
// 问题2:value 状态中没有改变的值导致组件渲染
console.log("setTheme 没有改变,其实我也不应该渲染的!!!");
return (
<div>
<button
onClick={() => setTheme((v) => (v === "light" ? "dark" : "light"))}
>
改变theme
</button>
</div>
);
}
function Other() {
// 问题1:和 value 状态无关的子组件渲染
console.log("Other render。其实我不应该重新渲染的!!!");
return <div>other组件,讲道理,我不应该渲染的!</div>;
}
问题1(整体重复渲染):Provider
组件包裹的子组件全部渲染
从这个例子可以看出来,用ThemeCtx.Provider
直接包裹子组件,每次ThemeCtx.Provider
组件渲染会导致所有子组件跟着重新渲染,原因是使用React.createElement(type, props: {}, ...)
创建的组件,每次props: {}
都会是一个新的对象。
问题2(局部重复渲染):使用useContext
导致组件渲染
createContext
是根据发布订阅模式来实现的,Provider
的value
值每次发生变化都会通知所有使用它的组件(使用useContext
的组件)重新渲染。
解决方案
上面我们分析了问题的根源,下面就开始解决问题。 同样先看一下优化后的例子:createContext性能优化。
import { useState, useContext, createContext, useMemo } from "react";
import { useWhyDidYouUpdate } from "ahooks";
import "./styles.css";
const ThemeCtx = createContext({});
export default function App() {
return (
<div className="App">
<ThemeProvide>
<ChangeButton />
<Theme />
<Other />
</ThemeProvide>
</div>
);
}
function ThemeProvide({ children }) {
const [theme, setTheme] = useState("dark");
return (
<ThemeCtx.Provider value={{ theme, setTheme }}>
{children}
</ThemeCtx.Provider>
);
}
function Theme() {
const ctx = useContext(ThemeCtx);
const { theme } = ctx;
useWhyDidYouUpdate("Theme", ctx);
return <div>{theme}</div>;
// return <ThemeCtx.Consumer>{({ theme }) => <div>{theme}</div>}</ThemeCtx.Consumer>;
}
function ChangeButton() {
const ctx = useContext(ThemeCtx);
const { setTheme } = ctx;
useWhyDidYouUpdate("Change", ctx);
/**
* 解决方案:使用 useMemo
*
*/
const dom = useMemo(() => {
console.log("re-render Change");
return (
<div>
<button
onClick={() => setTheme((v) => (v === "light" ? "dark" : "light"))}
>
改变theme
</button>
</div>
);
}, [setTheme]);
return dom;
}
function Other() {
console.log("Other render,其实我不应该重新渲染的!!!");
return <div>other,讲道理,我不应该渲染的!</div>;
}
解决问题1
把ThemeContext
抽离出来,子组件通过props
的children
属性传递进来。即使ThemeContext.Provider
重新渲染,children
也不会改变。这样就不会因为value
值改变导致所有子组件跟着重新渲染了。
解决问题2
通过上面的方式可以一刀切的解决整体重复渲染的问题,但局部渲染的问题就比较繁琐了,需要我们用useMemo
一个个的修改子组件,或者使用React.memo
把子组件更加细化。
参考
useContext深入学习
奇怪的useMemo知识增加了
react usecontext_React性能优化篇
createContext 你用对了吗?的更多相关文章
- React 之React.createContext
使用Context,可以跨越组件进行数据传递 import React from 'react'; import ReactDOM from 'react-dom'; const ThemeConte ...
- React的React.createContext()源码解析(四)
一.产生context原因 从父组件直接传值到孙子组件,而不必一层一层的通过props进行传值,相比较以前的那种传值更加的方便.简介. 二.context的两种实现方式 1.老版本(React16.x ...
- Uncaught TypeError: _react2.default.createContext is not a function
question is caused by react version, update your react version, it will be ok. use "npm update ...
- ASP.NET Core应用中如何记录和查看日志
日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性.我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger.Logger ...
- 学习ASP.NET Core, 怎能不了解请求处理管道[3]: 自定义一个服务器感受一下管道是如何监听、接收和响应请求的
我们在<服务器在管道中的"龙头"地位>中对ASP.NET Core默认提供的具有跨平台能力的KestrelServer进行了介绍,为了让读者朋友们对管道中的服务器具有更 ...
- 学习ASP.NET Core,怎能不了解请求处理管道[2]: 服务器在管道中的“龙头”地位
ASP.NET Core管道由注册的服务器和一系列中间件构成.我们在上一篇中深入剖析了中间件,现在我们来了解一下服务器.服务器是ASP .NET Core管道的第一个节点,它负责完整请求的监听和接收, ...
- 如果你想深刻理解ASP.NET Core请求处理管道,可以试着写一个自定义的Server
我们在上面对ASP.NET Core默认提供的具有跨平台能力的KestrelServer进行了详细介绍(<聊聊ASP.NET Core默认提供的这个跨平台的服务器——KestrelServer& ...
- ASP.NET Core真实管道详解[2]:Server是如何完成针对请求的监听、接收与响应的【上】
Server是ASP .NET Core管道的第一个节点,负责完整请求的监听和接收,最终对请求的响应同样也由它完成.Server是我们对所有实现了IServer接口的所有类型以及对应对象的统称,如下面 ...
- ASP.NET Core管道深度剖析(3):管道是如何处理HTTP请求的?
我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但是就具体的实现来说,由于其中涉及很多对象的交互,我想很少人能够地把它弄清楚.为了让读者 ...
随机推荐
- [第六篇]——云服务器之Spring Cloud直播商城 b2b2c电子商务技术总结
云服务器 云服务器(Elastic Compute Service, ECS)是一种简单高效.安全可靠.处理能力可弹性伸缩的计算服务. 云服务器管理方式比物理服务器更简单高效,我们无需提前购买昂贵的硬 ...
- 个人作业——CVPR顶会论文爬取
main.py #保存单个界面数据 def getInfo(url): # url='https://openaccess.thecvf.com/WACV2021' header={ 'User-Ag ...
- 【OI】Tex Quotes——UVa 272
题目: TEX is a typesetting language developed by Donald Knuth. It takes source text together with a fe ...
- 【OI】竖式问题分析与解答
题目:找出所有形如abc*de(三位数乘以两位数)的算式,使得在完整的竖式中,所有数字都属于一个特定的数字集合.输入数字集合(相邻数字之间没有空格),输出所有竖式.每个竖式前应有编号,之后应有一个空行 ...
- JMeter多个线程组的使用说明
Run Thread Groups consecutively (i.e one at a time),即独立运行每个线程组(例如在一个组运行结束后启动下一个) https://help.aliyun ...
- ubuntu系统安装docker
系统版本:Ubuntu 18.04 # 更新apt update # 安装依赖apt install apt-transport-https ca-certificates curl software ...
- 给你一个app,怎么测试
安装卸载 安装卸载路径是否能自己选择,在不同操作系统下(Android.ios)安装是否正常,能正常运行,安装的文件及文件夹是否写入了指定的目录里,安装来自不同来源的(应用宝.360助手)下是否正常. ...
- jmeter跑脚本的注意事项
指标主要看以下几点: 1.jmeter性能测试的报告,不要看平均响应时间,而是看90%响应时间,一般不能超过3s,超过3s则不符合标准2.响应时间超过3s就要优化,但不是平均响应时间,因为最小响应时间 ...
- django 高级扩展-中间件-上传图片-分页-富文本-celery
""" django 高级扩展 一.静态文件 1.css,js,json,图片,字体等 2.配置setting,在最底下设置静态文件目录,写入下面代码 #配置静态文件目录 ...
- python 建站教程
主端:安装nginx uwsgi django pymysql mysql安装mysql到http://repo.mysql.com/里面找 mysql57-community-release-el7 ...