React中useContext的基本使用和原理解析

news/2025/11/3 21:47:42/文章来源:https://www.cnblogs.com/techlaoli/p/19188389

React 中 useContext 的使用方法

在 React 中,useContext 是一个内置的 Hook,用于在函数组件中轻松访问 Context(全局公共状态),避免了手动逐层传递 props 的复杂性。它依赖于 Context API,通过 Provider 提供数据,后代组件通过 useContext 消费数据。以下是详细的使用方法和步骤,基于 React 官方指南和实践经验。

1. 创建 Context 对象

首先需要使用 React.createContext 创建一个 Context 对象。这个对象包含 ProviderConsumer 组件,但 useContext 简化了消费过程。

import React from 'react';
// 创建Context,可设置默认值(可选)
const MyContext = React.createContext(defaultValue);
  • defaultValue 是当组件上方无 Provider 时的回退值,通常设为 null 或初始状态。

2. 使用 Provider 提供数据

在父组件中,用 <Context.Provider> 包裹子组件,并通过 value 属性传递数据。Provider 必须位于调用 useContext 的组件之上。

import React from 'react';
import ChildComponent from './ChildComponent';
import MyContext from './MyContext';function ParentComponent() {const sharedData = { theme: 'dark', user: 'Alice' }; // 共享数据return (<MyContext.Provider value={sharedData}><ChildComponent /> {/* 后代组件可访问sharedData */}</MyContext.Provider>);
}
  • 注意​:Provider 的 value 变化时,所有消费该 Context 的组件会自动重新渲染​​。

3. 在后代组件中使用 useContext 消费数据

在后代组件中,导入 Context 对象并调用 useContext,直接获取 Provider 提供的 value

import React, { useContext } from 'react';
import MyContext from './MyContext'; // 导入父组件中的Contextfunction ChildComponent() {const publicData = useContext(MyContext); // 调用useContext获取数据return (<div><p>当前主题: {publicData.theme}</p>{/* 示例:渲染图片或其他UI */}<img src="image-path" alt="示例" style={{ width: '50px', marginLeft: '10px' }} /></div>);
}
  • 关键点​:
    • useContext(MyContext) 返回最近的 Provider 的 value;若无 Provider,则返回 defaultValue
    • 代码简洁,无需嵌套 <Context.Consumer>
    • Context 变化时,React 会触发组件重新渲染,确保数据最新​​。

4. 完整代码示例

整合以上步骤,一个简单应用:

// 文件: Context.js
import React from 'react';
export const ThemeContext = React.createContext({ theme: 'light' });// 文件: App.js (父组件)
import React from 'react';
import { ThemeContext } from './Context';
import Child from './Child';function App() {return (<ThemeContext.Provider value={{ theme: 'dark' }}><Child /></ThemeContext.Provider>);
}// 文件: Child.js (后代组件)
import React, { useContext } from 'react';
import { ThemeContext } from './Context';function Child() {const { theme } = useContext(ThemeContext);return <div>当前主题: {theme}</div>; // 输出: 当前主题: dark
}

在类组建中,useContext 的使用方法

在类组件中使用 Context 有两种方式:

  1. 使用 static contextType 属性(只能订阅单一 Context)
  2. 使用 Context.Consumer(可订阅多个 Context)

而在函数组件中,我们使用 useContext 钩子(可订阅多个 Context)。

下面我将详细说明类组件中使用 Context 的方法,并对比函数组件中的使用差异。

1、使用 static contextType(单一 Context 订阅)

步骤:

  • 创建 Context:const MyContext = React.createContext(defaultValue);
  • 在类组件中通过 static contextType = MyContext; 指定要订阅的 Context
  • 通过 this.context 访问 Context 的值

示例代码

import React from 'react';// 创建Context
const ThemeContext = React.createContext('light');class MyClassComponent extends React.Component {static contextType = ThemeContext; // 关键:静态属性赋值render() {const theme = this.context; // 通过this.context访问return <div>当前主题: {theme}</div>;}
}// 在父组件中提供Context
function App() {return (<ThemeContext.Provider value="dark"><MyClassComponent /></ThemeContext.Provider>);
}

2、使用 Context.Consumer(支持多个 Context)

步骤:

  • 在类组件的 render 方法中,使用 <MyContext.Consumer> 组件包裹
  • 内部使用函数作为子元素(render prop 模式)

示例代码:

import React from 'react';// 创建两个Context
const ThemeContext = React.createContext('light');
const UserContext = React.createContext('Guest');class MyClassComponent extends React.Component {render() {return (// 消费多个Context<ThemeContext.Consumer>{theme => (<UserContext.Consumer>{user => (<div>主题: {theme}, 用户: {user}</div>)}</UserContext.Consumer>)}</ThemeContext.Consumer>);}
}// 在父组件中提供多个Context
function App() {return (<ThemeContext.Provider value="dark"><UserContext.Provider value="Alice"><MyClassComponent /></UserContext.Provider></ThemeContext.Provider>);
}

3、类组件与函数组件使用 Context 的主要区别

特性 类组件 函数组件
订阅方式 1. static contextType + this.context(单一)2. Context.Consumer(支持多个) useContext 钩子(支持多个)
多个 Context 使用 使用 Context.Consumer 嵌套较深 直接多次调用 useContext,简洁清晰
代码简洁性 相对冗长,尤其是多个 Context 时 非常简洁
组件类型限制 static contextType 仅适用于类组件(引用[1]) useContext 仅适用于函数组件
动态更新 当 Context 更新时,组件都会重新渲染 同样重新渲染,但可通过 React.memo 优化

4、useContext 实现原理详解

useContext 的实现原理基于 React 的 上下文机制(Context) 和 ​订阅-发布模式​,主要涉及三个核心环节:

1. Context 对象的内部结构

每个通过 createContext() 创建的 Context 对象包含以下关键属性:

const MyContext = React.createContext(defaultValue);
// 内部结构:
{_currentValue: defaultValue,  // 当前值存储_threaded: true,             // 标识当前渲染线程Provider: { ... },           // Provider 组件Consumer: { ... },           // Consumer 组件_currentRenderer: null,      // 当前渲染器_globalName: null,           // 全局名称_subscribe: function() { ... } // 订阅函数
}

核心是 _currentValue (存储当前值) 和 _subscribe (管理订阅者链表)

2. 值读取与订阅机制

当调用 useContext(MyContext) 时:

function useContext(Context) {// 1. 从 Context._currentValue 读取当前值const value = readContext(Context); // 2. 将当前组件添加到订阅链表subscribeToContext(Context, currentlyRenderingFiber);return value; // 返回上下文值
}

currentlyRenderingFiber 是 React 内部的一个​全局变量​,用于指向当前正在执行的函数组件所对应的 ​Fiber 节点​。它的主要作用是在函数组件渲染过程中为 Hooks 提供访问当前组件状态的桥梁。

具体过程:

  1. 读取值​:直接访问 Context._currentValue 获取最新值
  2. 建立订阅​:将当前函数组件对应的 Fiber 节点添加到 Context 的订阅者链表
    • 通过 currentlyRenderingFiber.dependencies 链表维护订阅关系
    • 每个依赖项包含 context 指针和订阅状态

3. 更新触发流程

当 Provider 的值更新时:

<MyContext.Provider value={newValue}>
// 1. 更新 Context._currentValue = newValue
// 2. 遍历订阅者链表 (Context._subscribe)
// 3. 标记所有订阅组件的 Fiber 节点为需要更新
// 4. 触发重新渲染

关键点:

  • 批量更新​:React 会合并多个 Context 更新,避免频繁渲染
  • 精准更新​:只更新订阅该 Context 的组件(通过 Fiber 依赖链)
  • 默认值处理​:无 Provider 时返回 createContext(defaultValue) 的默认值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/954928.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JDK的安装过程

第一步:右击“此电脑”,在显示的菜单中单击“属性”,在“系统信息”中点击“高级系统设置” 第二步:在弹出的“系统属性”对话框中,选择“高级”选项卡中的“环境变量” 第三步:在弹出的对话框中,单击“系统变量…

阅读笔记0

第3章:基本工具 强调善用工具能大幅提升效率,工具是程序员的“第二双手”。文本编辑器:不止是打字,要熟练使用快捷键、自定义配置,甚至通过插件扩展功能(如代码补全、语法检查)。 ​ 版本控制:用Git等工具管理…

File文件操作

路径用//或者\ file可以代表文件也可以代表文件夹文件对象路径可以写相对路径也可以写绝对路径,相对路径默认是是从当前文件的项目名称下寻找 从javatest下找createnewfile创建完文件会返回true exist同理 调用方法都是…

越南航空数据泄露事件深度解析

2025年越南航空遭遇重大数据泄露事件,730万客户信息遭黑客组织窃取,包括邮箱、姓名、电话号码等敏感数据。本文详细分析事件经过、受影响数据类型及防护建议。越南航空数据泄露事件 事件概述 2025年10月,自称"…

P11261 [COTS 2018] 直方图 Histogram

P11261 [COTS 2018] 直方图 Histogram 题解看了这篇题解懂了的,大家也可以去看看。 以及,后来自己想出来了单调栈解法,看题解里似乎没有这个解法,所以交一发题解。题目传送门 欢迎光临我的博客 1.笛卡尔树做法 如果…

2025csp-j游记(废物版)

"生活就是在迷茫中前进" 11月1日周六考的试,周一已经有点忘了。。 DAY 初赛 j组初赛自我感觉良好,就是考完前几分钟ccf突然改代码,但只是加了个int n难绷。s组感觉自己没开智。更绝望的是隔壁班有个gesp保…

leetcode55. 跳跃游戏 45. 跳跃游戏 II

leetcode55. 跳跃游戏 45. 跳跃游戏 II55. 跳跃游戏 45. 跳跃游戏 II 我写的第一份通过的代码,问题在于重复更新浪费不少时间,内层循环可能会重复更新许多已经确定不是最优解的位置。class Solution {public int j…

个体户办理食品经营须知

非常好的问题,这两个问题都属于实际审批中高频且容易出错的关键点。下面我根据你提供的两份文件—— 📘《食品经营许可审查通则》(国家标准) 📗《山东省食品经营许可审查细则》(山东省地方标准) 为你做专业、…

redux-thunk和createAsyncThunk

你提到了一个非常有趣且常见的观点! 许多开发者确实认为,从“编写和阅读”异步逻辑的角度来看,手写 redux-thunk 的 async (dispatch) => {...} 形式,比 createAsyncThunk 更加直接和直观。⚖️ 两种异步写法的…

2025.11.3——1绿1蓝

普及+/提高 P1353 [USACO08JAN] Running S 简单DP 提高+/省选 P6880 [JOI 2020 Final] 奥运公交 / Olympic Bus T3,场切。

Next.js路由段配置选项笔记

前言 大家好,我是曦远。 本来是想发昨晚写好的 starblog 管理后台重构文章的 结果打开 blog 才发现忘记提交了😂 所以写一篇新的吧 正好最近正在大量使用 Next.js 我发现部署后的首次渲染很慢,才意识到「预热」这个…

2025.11.3 - A

今天java和数据结构,感觉挺好的。

【每日一面】实现一个深拷贝函数

基础问答 问:知道浅拷贝和深拷贝吗?为什么要用深拷贝? 答:拷贝,可以认为是赋值,对于 JavaScript 中的基础类型,如 string, number, null, boolean, undefined, symbol 等,在赋值给一个变量的时候,是直接拷贝值…

【AI说Rust 01】Rust 的学习路线

Rust 以其卓越的性能和内存安全性吸引了众多开发者。虽然它的学习曲线相对陡峭,但一份清晰的学习路线能让你事半功倍。下面这个路线图汇总了主流的学习阶段和资源,希望能帮你从零开始,逐步进阶。 flowchart LRA[Rus…

若依后端验证码实现

先去看前端的 参考详细讲解视频:https://www.bilibili.com/video/BV1HT4y1d7oA?spm_id_from=333.788.player.switch&vd_source=886219f6fb49f459fbfc8b80a8b39f3f&p=3 登录 前端请求为http://localhost/dev-…

解码LVGL事件

LVGL 事件系统 事件是 LVGL 响应用户操作(如点击、滑动)或控件状态变化的核心机制,通过 “事件绑定 - 回调函数” 实现交互逻辑。 事件核心特点多绑定支持:一个回调函数可绑定多个对象(如一个 “计数回调” 绑定两…

11.3号学习内容

阅读模型压缩的论文| https://doi.org/10.48550/arXiv.2010.03954 | header | | ----------------------------------------- | ------ | | | |

P11771 题解

blog。虽然糖丸了,但是卡了还是半天卡过去了。感谢出题人开 2s /kt!!最显然的暴力是,考虑直接算每个 \(i,j,k\) 的贡献。\(p_{i}\le p_k\wedge p_j\le p_k\):贡献为 \(0\)。 \(p_{i}>p_k\wedge p_j\le p_k\):…

MySQL排序算法

一、概述 ORDER BY的核心功能,是按照指定的单个或多个字段,对SELECT查询返回的结果集进行升序(ASC,默认)或降序(DESC)排列,以满足业务对数据有序性的需求。但要判断ORDER BY的实际执行效率,最直接的工具是EXP…

CSP-S 2025 饭堂寄

省流:\(100+48+0+0=148\),爆炸。 Day -2 考试前几天竟然发现有些感冒了。 Day -1 考试前一天晚上睡得比较晚,因为回到家都接近 11 点钟了。 Day 1 早上起来已经 9:30 了,起来开始打板子,其实这个时候已经感觉状态…