前言

createContext是 react 提供的用于全局状态管理的一个 api,我们可以通过Provider组件注入状态,用Consumer组件或者useContextapi 获取状态(推荐使用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是根据发布订阅模式来实现的,Providervalue值每次发生变化都会通知所有使用它的组件(使用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抽离出来,子组件通过propschildren属性传递进来。即使ThemeContext.Provider重新渲染,children也不会改变。这样就不会因为value值改变导致所有子组件跟着重新渲染了。

解决问题2

通过上面的方式可以一刀切的解决整体重复渲染的问题,但局部渲染的问题就比较繁琐了,需要我们用useMemo一个个的修改子组件,或者使用React.memo把子组件更加细化。

参考

useContext深入学习

奇怪的useMemo知识增加了

react usecontext_React性能优化篇

createContext 你用对了吗?的更多相关文章

  1. React 之React.createContext

    使用Context,可以跨越组件进行数据传递 import React from 'react'; import ReactDOM from 'react-dom'; const ThemeConte ...

  2. React的React.createContext()源码解析(四)

    一.产生context原因 从父组件直接传值到孙子组件,而不必一层一层的通过props进行传值,相比较以前的那种传值更加的方便.简介. 二.context的两种实现方式 1.老版本(React16.x ...

  3. 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 ...

  4. ASP.NET Core应用中如何记录和查看日志

    日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性.我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger.Logger ...

  5. 学习ASP.NET Core, 怎能不了解请求处理管道[3]: 自定义一个服务器感受一下管道是如何监听、接收和响应请求的

    我们在<服务器在管道中的"龙头"地位>中对ASP.NET Core默认提供的具有跨平台能力的KestrelServer进行了介绍,为了让读者朋友们对管道中的服务器具有更 ...

  6. 学习ASP.NET Core,怎能不了解请求处理管道[2]: 服务器在管道中的“龙头”地位

    ASP.NET Core管道由注册的服务器和一系列中间件构成.我们在上一篇中深入剖析了中间件,现在我们来了解一下服务器.服务器是ASP .NET Core管道的第一个节点,它负责完整请求的监听和接收, ...

  7. 如果你想深刻理解ASP.NET Core请求处理管道,可以试着写一个自定义的Server

    我们在上面对ASP.NET Core默认提供的具有跨平台能力的KestrelServer进行了详细介绍(<聊聊ASP.NET Core默认提供的这个跨平台的服务器——KestrelServer& ...

  8. ASP.NET Core真实管道详解[2]:Server是如何完成针对请求的监听、接收与响应的【上】

    Server是ASP .NET Core管道的第一个节点,负责完整请求的监听和接收,最终对请求的响应同样也由它完成.Server是我们对所有实现了IServer接口的所有类型以及对应对象的统称,如下面 ...

  9. ASP.NET Core管道深度剖析(3):管道是如何处理HTTP请求的?

    我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但是就具体的实现来说,由于其中涉及很多对象的交互,我想很少人能够地把它弄清楚.为了让读者 ...

随机推荐

  1. golang sync.noCopy 类型 —— 初探 copylocks 与 empty struct

    问题引入 学习golang(v1.16)的 WaitGroup 代码时,看到了一处奇怪的用法,见下方类型定义: type WaitGroup struct { noCopy noCopy ... } ...

  2. 从零开始学习SQL SERVER(2)--- 基本操作及语句

    声明:仅为本人随笔及经验之谈,有错误敬请指出. # 后的文字为注释 Microsoft SQL Server Management Studio 中的SQL命令 添加数据库 1 CREATE DATA ...

  3. 【第十五篇】- Maven 依赖管理之Spring Cloud直播商城 b2b2c电子商务技术总结

    Maven 依赖管理 Maven 一个核心的特性就是依赖管理.当我们处理多模块的项目(包含成百上千个模块或者子项目),模块间的依赖关系就变得非常复杂,管理也变得很困难.针对此种情形,Maven 提供了 ...

  4. Spring5框架学习笔记

    Spring5学习笔记 介绍: 1.引入相应jar包 导入: ps:网上下载教程: https://repo.spring.io/release/org/springframework/spring/ ...

  5. RabbitMQie消息列队整理

    使用方法过程,这儿只做了windows平台教程 先安装Erlang 编程软件,然后设置环境变量,在安装RabbimMQ ,这儿我下载了一个版本不行,后来换了最新版就好了,以后在使用过程 中如果有问题 ...

  6. Linux系列(32) - rpm命令管理之RPM查询(4)

    RPM包默认安装位置 RPM包默认安装路径 /etc/ 配置文件安装目录 /usr/bin/ 可执行的命令安装目录 /usr/lib/ 程序所使用的函数库保存位置 /usr/share/doc/ 基本 ...

  7. 详细的vsftpd配置

    环境:CentOS 5.0 操作系统一.安装:1.安装Vsftpd服务相关部件:[root@KcentOS5 ~]# yum install vsftpd*Dependencies Resolved= ...

  8. P1912-[NOI2009]诗人小G【四边形不等式,单调队列】

    正题 题目链接:https://www.luogu.com.cn/problem/P1912 题目大意 给出\(n\)个字符串,把这些字符串依次用空格(算一个长度)连接分成若干段,若一段长度为\(x\ ...

  9. SpringBoot之SpringSecurity权限注解在方法上进行权限认证多种方式

    前言 Spring Security支持方法级别的权限控制.在此机制上,我们可以在任意层的任意方法上加入权限注解,加入注解的方法将自动被Spring Security保护起来,仅仅允许特定的用户访问, ...

  10. Python3入门系列之-----函数

    什么是函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也可以自己 ...