沐游虞笔记
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 基础篇

    • 序章:React课程介绍
    • JSX 基础语法
    • React 基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React-redux 介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
      • Context 要解决的问题
      • Context 的用法
        • displayName
        • 默认值
        • 多个上下文环境
      • Context 相关 Hook
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • React和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟 DOM
    • React 整体架构
    • React 渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduler调度普通任务
    • Scheduler调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit 工作流程
    • lane模型
    • React 中的事件
    • Hooks原理
    • useState和useReducer
    • effect相关hook
    • useCallback和useMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailout和ContextAPI
    • 性能优化对日常开发启示
  • react
  • 就业篇
luzhichang
2024-10-17
目录

Context

# 4. Context

有关 Context,这是一个非常重要的知识点,甚至我们之后在书写 mini-react、mini-react-router、mini-redux 时都会用到的一个知识点,所以这一小节,我们就来好好看一下 Context 的相关内容,主要包含以下几个点:

  • Context 要解决的问题
  • Context 的用法
  • Context 相关 Hook

# Context 要解决的问题

首先来看一下 Context 要解决的问题。正常来讲,我们单页应用中的组件会形成一个像组件树一样结构,当内部组件和组件之间要进行数据传递时,就免不了一层一层先传递到共同的父组件,然后再一层一层传递下去。

image-20221130135702192

假设 subComA-1 组件的状态数据要传递给 subComB-2 组件,应该怎么做?

根据我们前面所讲的单项数据流的规则,那么数据应该被提升到 App 根组件,然后通过 props 一层一层的传递给下面的子组件,最终 subComA-1 拿到所需要的数据;如果 subComA-1 组件需要修改传递下来的数据,那么该组件就还需接收从 App 根组件一层一层传递下来的能够修改数据的方法。

官方在“何时使用 Context”这一小节也举了一个形象的例子: https://zh-hans.reactjs.org/docs/context.html#when-to-use-context

因此,简单一句话概括 Context,那就是解决组件之间数据共享的问题,避免一层一层的传递。

此时你肯定会想,前面的 redux 不就已经解决了这个问题么?没错,实际上 redux 的实现原理就是基于 Context 所进行的一层封装。

Context 如果直接翻译成中文,会被翻译成“上下文”,这其实在软件领域很常见的一个词,比如前面我们也学习过“执行上下文”,所谓上下文,往往指的是代码执行时所需的数据环境信息。

实际上生活中也有类似的场景,例如你在厨房做饭,你的周围有各种做饭所需的厨具,例如菜刀、案板、锅、瓢等,这些工具构成了一个(上下文)环境,当你要做饭要用到某一样工具时,直接从这个环境中就能过获取到。

image-20221130135751769

# Context 的用法

React 官方对于 Context 的用法,分为旧版 API 和新版 API,有关旧版 API 的用法,本文档就不再赘述,如果有需要的同学,可以参阅:https://zh-hans.reactjs.org/docs/legacy-context.html

这里我们来看一下新版 API 的使用,示例如下:

// src/context/index.js

import React from "react";

const MyContext = React.createContext();
export default MyContext;

首先,使用 React.createContext API 创建的一个上下文对象,该对象里面会提供两个组件,分别是 Provider 和 Consumer,表示数据的提供者和消费者。

// App.jsx
import ChildCom1 from "./components/ChildCom1";
import MyContext from "./context";
import { useState } from "react";

const { Provider } = MyContext;

function App() {
  const [count, setCount] = useState(0);
  return (
    <Provider value={{ count, setCount }}>
      <div style={{
        border: '1px solid',
        width: "250px",
      }}>
        <ChildCom1 />
      </div>
    </Provider>
  );
}

export default App;

在根组件 App.jsx 中,我们设置了一个根组件的状态数据 count,然后从 MyContext 中解构出 Provider 组件来作为数据的提供者,value 属性用来设置要提供的数据。

// components/ChildCom1.jsx

import React from 'react';
import ChildCom2 from "./ChildCom2"
import ChildCom3 from "./ChildCom3"

function ChildCom1() {
    return (
        <div>
            ChildCom1
            <ChildCom2/>
            <ChildCom3/>
        </div>
    );
}

export default ChildCom1;

在 ChildCom1 子组件中,无需再像中转站一样接受父组件的数据然后又传递给 ChildCom2 和 ChildCom3 组件。

// components/ChildCom2.jsx

import React from 'react';
import MyContext from "../context";

const { Consumer } = MyContext;

function ChildCom2() {
    return (
        <Consumer>
            {(context) => (
                <div
                    style={{
                        border: '1px solid',
                        width: "200px",
                        userSelect: 'none'
                    }}
                    onClick={() => context.setCount(context.count + 1)}
                >
                    ChildCom2
                    <div>count:{context.count}</div>

                </div>
            )}
        </Consumer>
    );
}

export default ChildCom2;

ChildCom2 组件是一个函数组件,函数组件想要获取 Context 上下文中的数据,需要使用 Consumer 组件,这种方法需要一个函数作为子元素,这个函数接收当前的 context 值,并返回一个 React 节点。

import React, { Component } from 'react'
import MyContext from "../context";

export default class ChildCom3 extends Component {

    static contextType = MyContext;

    render() {
        return (
            <div
                style={{
                    border: '1px solid',
                    width: "200px",
                    userSelect: 'none'
                }}
                onClick={()=>this.context.setCount(this.context.count + 2)}
            >
                ChildCom3
                <div>count:{this.context.count}</div>
            </div>
        )
    }
}

ChildCom3 组件是一个类组件,类组件当然也可以使用上面 Consumer 的方式来获取上下文中的数据,但对于类组件而言,还可以使用 contextType 的方式。挂载在 class 上的 contextType 属性可以赋值为由 React.createContext( ) 创建的 Context 对象。

整体的组件树结构图如下:

image-20221130135829210

最后我们来看一下效果:

iShot_2022-11-29_16.14.33

# displayName

如果安装了 React Developer Tools 工具,那么在 components 选项卡中可以看到如下的组件树结构,默认的名字就为 Context.Provider 和 Context.Consumer

image-20221130135908369

通过设置 displayName 可以修改显示名字,如下:

import React from "react";

const MyContext = React.createContext();
MyContext.displayName = 'counter';
export default MyContext;
image-20221130135957667

# 默认值

Context 上下文环境可以设置默认值,如下:

import React from "react";

const MyContext = React.createContext({
  name: "xiejie",
  age: 18,
});
MyContext.displayName = "counter";
export default MyContext;

此时就**不再需要 Provider 组件来提供数据**了,子组件可以直接消费上下文环境的默认数据。

// App.jsx

import ChildCom1 from "./components/ChildCom1";
function App() {
  return (
      <div style={{
        border: '1px solid',
        width: "250px",
      }}>
        <ChildCom1 />
      </div>
  );
}

export default App;

根组件 App 已经不再需要使用 Provider 组件来提供初始数据。

import React from 'react';
import MyContext from "../context";

const { Consumer } = MyContext;


function ChildCom2() {
    return (
        <Consumer>
            {(context) => (
                <div
                    style={{
                        border: '1px solid',
                        width: "200px",
                        userSelect: 'none'
                    }}
                >
                    ChildCom2
                    <div>name:{context.name}</div>

                </div>
            )}
        </Consumer>

    );
}

export default ChildCom2;
import React, { Component } from 'react'
import MyContext from "../context";

export default class ChildCom3 extends Component {

    static contextType = MyContext;

    render() {
        return (
            <div
                style={{
                    border: '1px solid',
                    width: "200px",
                    userSelect: 'none'
                }}
            >
                ChildCom3
                <div>age:{this.context.age}</div>
            </div>
        )
    }
}

无论是 ChildCom2 还是 ChildCom3,都能够直接从上下文中获取默认的上下文数据。

效果如下:

image-20221130140025242

# 多个上下文环境

在上面的示例中,我们示例的都是一个 Context 上下文环境,这通常也够用了,但是这并不意味着只能提供一个上下文环境,我们可以创建多个上下文环境,示例如下:

import React from "react";

export const MyContext1 = React.createContext();
export const MyContext2 = React.createContext();

首先,我们导出两个上下文环境,接下来在 App.jsx 中,使用多个 Provider 作为数据的提供者

import ChildCom1 from "./components/ChildCom1";
import { MyContext1, MyContext2 } from "./context"
function App() {
  return (
    <MyContext1.Provider value={{ a: 1, b: 2 }}>
      <MyContext2.Provider value={{ b: 10, c: 20 }}>
        <div style={{border: '1px solid',width: "250px",}}>
          <ChildCom1 />
        </div>
      </MyContext2.Provider>
    </MyContext1.Provider>

  );
}

export default App;

之后在 ChildCom2 和 ChildCom3 中同样也使用多个 Consumer 来消费不同上下文中的数据

import React from 'react';
import { MyContext1, MyContext2 } from "../context";
function ChildCom2() {
    return (
        <MyContext1.Consumer>
            {
                (context1) => (
                    <MyContext2.Consumer>
                        {(context2) => (
                            <div
                                style={{
                                    border: '1px solid',
                                    width: "200px",
                                    userSelect: 'none'
                                }}
                            >
                                ChildCom2
                                <div>a : {context1.a}</div>
                                <div>b : {context1.b}</div>
                                <div>c : {context2.c}</div>
                            </div>
                        )}
                    </MyContext2.Consumer>
                )
            }
        </MyContext1.Consumer>
    );
}

export default ChildCom2;
import React, { Component } from 'react'
import { MyContext1, MyContext2 } from "../context";

export default class ChildCom3 extends Component {
    render() {
        return (
            <MyContext1.Consumer>
                {
                    (context1) => (
                        <MyContext2.Consumer>
                            {
                                (context2) => (
                                    <div
                                        style={{
                                            border: '1px solid',
                                            width: "200px",
                                            userSelect: 'none'
                                        }}
                                    >
                                        ChildCom3
                                        <div>a:{context1.a}</div>
                                        <div>b:{context2.b}</div>
                                        <div>c:{context2.c}</div>
                                    </div>
                                )
                            }
                        </MyContext2.Consumer>
                    )
                }
            </MyContext1.Consumer>

        )
    }
}

效果如下:

image-20221130140046232

在消费上下文里面的数据时,如果回调函数中的参数名相同,则从最近的上下文中去获取数据。

# Context 相关 Hook

在 React Hook API 中,为我们提供了一个更加方便的 useContext 钩子函数,该 Hook 接收一个由 React.createContext API 创建的上下文对象,并返回该 context 的当前值。

例如:

import { useContext } from 'react';
import { MyContext1 } from "../context";
function ChildCom2() {
    const { a, b, c } = useContext(MyContext1)
    return (
        <div
            style={{
                border: '1px solid',
                width: "200px",
                userSelect: 'none'
            }}
        >
            ChildCom2
            <div>a : {a}</div>
            <div>b : {b}</div>
            <div>c : {c}</div>
        </div>
    );
}

export default ChildCom2;

useContext(MyContext) 相当于类组件中的 static contextType = MyContext 或者 <MyContext.Consumer>,但是我们**仍然需要在上层组件树中使用 <MyContext.Provider> 来为下层组件提供 context**。


-EOF-

Ref
Render Props

← Ref Render Props→

Theme by Vdoing | Copyright © 2021-2024 蜀ICP备2024068710号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式