React 的 Context(上下文)主要解决 跨层级组件传值(Props Drilling) 问题,即:父组件的数据需要经过很多层组件,最终传给深层子组件。
1. 什么是 Context
没有 Context:
javascript
function App() {
const theme = 'dark';
return <Parent theme={theme} />;
}
function Parent({ theme }) {
return <Child theme={theme} />;
}
function Child({ theme }) {
return <GrandChild theme={theme} />;
}
function GrandChild({ theme }) {
return <div>{theme}</div>;
}
数据:
App → Parent → Child → GrandChild
即使 Parent 和 Child 不使用 theme,也必须传。
这种叫:
Props Drilling(属性穿透)
Context 用于解决这个问题。
2. Context 使用步骤(跨层传值)
React Context 核心 API:
scss
createContext()
Provider
useContext()
第一步:创建 Context
创建:
javascript
// ThemeContext.js
import { createContext } from "react";
export const ThemeContext = createContext();
默认值:
scss
createContext("light");
如果 Provider 没包裹,会取默认值。
第二步:Provider 提供数据
根组件:
javascript
import { ThemeContext } from "./ThemeContext";
function App() {
const theme = "dark";
return (
<ThemeContext.Provider value={theme}>
<Parent />
</ThemeContext.Provider>
);
}
Provider:
提供共享数据
value:
所有子组件可访问
第三步:子组件获取
深层组件:
javascript
import { useContext } from "react";
import { ThemeContext } from "./ThemeContext";
function GrandChild() {
const theme =
useContext(ThemeContext);
return <div>{theme}</div>;
}
输出:
dark
即:
scss
App
↓
Provider(value=dark)
↓
Parent
↓
Child
↓
GrandChild
↓
useContext()
直接拿值,不需要层层传。
完整例子
javascript
import React,
{
createContext,
useContext,
useState
}
from "react";
const UserContext =
createContext();
function App(){
const [user,setUser]=
useState("Tom");
return(
<UserContext.Provider
value={{user,setUser}}
>
<Home/>
</UserContext.Provider>
)
}
function Home(){
return <Profile/>
}
function Profile(){
const {user,setUser}
=
useContext(UserContext);
return(
<>
<p>{user}</p>
<button
onClick={()=>
setUser("Jerry")
}
>
修改
</button>
</>
)
}
结果:
点击按钮:
Tom → Jerry
所有使用 Context 的组件都会更新。
3. Context 优点
(1)解决 Props Drilling
以前:
css
App
↓
A
↓
B
↓
C
↓
D
需要:
ini
<A user={user}/>
<B user={user}/>
...
Context:
scss
useContext(UserContext)
直接取。
优势最大。
(2)全局共享状态
适合:
用户信息
主题
权限
语言
Token
购物车
例如:
theme
locale
loginUser
多个页面共用。
(3)代码更简洁
减少:
props.xxx.xxx
改成:
scss
useContext()
(4)避免层层维护
修改组件层级:
以前:
css
App→A→B→C
变:
css
App→X→A→B→C
props 全改。
Context 不需要。
4. Context 缺点
虽然方便,但不是状态管理库。
缺点1:性能问题(最大问题)
Context 数据变化:
ini
value={{
user,
setUser
}}
user 改变:
所有 useContext 的组件重新渲染
即使组件没用到变化的数据。
例如:
css
UserContext
├── Header
├── Menu
├── Profile
└── Footer
修改:
user.name
可能:
css
Header 重渲染
Menu 重渲染
Footer 重渲染
性能下降。
优化:
scss
const value =
useMemo(()=>({
user,
setUser
}),[user])
或拆多个 Context。
缺点2:不适合复杂状态
复杂业务:
异步请求
缓存
分页
撤销
loading
Context 管理会混乱。
这时推荐:
- Redux Toolkit
- Zustand
- MobX
缺点3:调试困难
Redux:
能看:
perl
dispatch
state变化
时间旅行
Context:
没有。
缺点4:Provider 嵌套地狱
容易出现:
xml
<AuthProvider>
<ThemeProvider>
<UserProvider>
<CartProvider>
<App/>
</CartProvider>
</UserProvider>
</ThemeProvider>
</AuthProvider>
可读性下降。
5. Context 适用场景
适合(推荐):
① 登录用户信息
userInfo
token
role
② 主题切换
暗黑模式:
bash
dark/light
最经典。
③ 多语言
zh
en
jp
④ 权限控制
isAdmin
permission
⑤ 全局配置
API地址
系统配置
6. 不适合场景
不推荐:
大型复杂状态
例如:
订单系统
即时聊天
性能平台
工作流系统
像你做的云鉴性能平台:
性能查询
分页
筛选
缓存
异步请求
报告状态
更适合:
- Redux Toolkit
- Zustand
- React Query + Context
- MobX
而不是纯 Context。
7. Context 与 Redux 区别
| 对比 | Context | Redux |
|---|---|---|
| 学习成本 | 低 | 高 |
| 跨层传值 | √ | √ |
| 状态管理 | 一般 | 强 |
| 性能优化 | 较差 | 好 |
| 调试 | 差 | 强 |
| 异步 | 弱 | 强 |
| 大项目 | 不推荐 | 推荐 |
8. 一句话总结
Context 本质:
跨组件共享数据机制
适合:
主题
登录
权限
语言
配置
不适合:
复杂全局状态管理
复杂项目优先考虑:
Redux Toolkit
Zustand
React Query