你是不是也遇到过?
- 组件层级一深,props 传到怀疑人生...
- 改个状态,祖孙八代组件都得改...
- 一个主题色切换,改半天全局都没反应...
别怕!
这篇用最简单的「主题切换」例子,一口气教会你:
- 什么是
useContext
Provider
怎么用- 自定义
useTheme
怎么封装 - 组件怎么消费全局状态
先说结论:useContext
解决啥问题?
咱都知道,React 核心原则是:
单向数据流,父传子,props 一层层往下传。
可是------
当组件嵌套很深时:
xml
<App>
<Parent>
<Child>
<GrandChild>
<GreatGrandChild />
</GrandChild>
</Child>
</Parent>
</App>
如果最外层有个状态,最里层也需要用到,怎么办?
要么 props 一层层传(麻烦!)
要么用全局状态管理(Context!)
所以:
Context
本质就是:在组件树中共享状态,不用 props 一层层传递。
Context
三步走流程
别背概念,直接记死
✅ 第一步:创建上下文对象
js
import { createContext } from 'react';
export const ThemeContext = createContext('light'); // 默认值
✅ 第二步:用 Provider 包住要用到它的组件
jsx
import { ThemeContext } from './ThemeContext';
<ThemeContext.Provider value={theme}>
<App />
</ThemeContext.Provider>
✅ 第三步:在需要的地方用 useContext
拿到状态
jsx
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
const theme = useContext(ThemeContext);
完事儿。
看个完整主题切换示例
直接上代码,一把梭
ThemeContext.js
:创建上下文
jsx
import { createContext } from 'react';
// 默认值是 'light'
export const ThemeContext = createContext('light');
App.jsx
:声明全局 Provider
jsx
import { useState } from 'react';
import { ThemeContext } from './ThemeContext';
import Page from './components/Page';
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={theme}>
<Page />
<button onClick={() => setTheme('dark')}>
切换主题
</button>
</ThemeContext.Provider>
);
}
export default App;
hooks/useTheme.js
:封装自定义 Hook
jsx
import { useContext } from 'react';
import { ThemeContext } from '../ThemeContext';
export function useTheme() {
return useContext(ThemeContext);
}
components/Page.jsx
:使用自定义 Hook
jsx
import { useTheme } from '../hooks/useTheme';
import Child from './Child';
const Page = () => {
const theme = useTheme();
return (
<>
<div>当前主题:{theme}</div>
<Child />
</>
);
};
export default Page;
components/Child.jsx
:子组件一样能用
jsx
import { useTheme } from '../hooks/useTheme';
const Child = () => {
const theme = useTheme();
return (
<div className={theme}>
我是 Child,当前主题:{theme}
</div>
);
};
export default Child;
🎉 跑起来后会发生什么?
- 点击
切换主题
按钮,App
的theme
状态变了 ThemeContext.Provider
传给下面所有子组件的新值- 所有用
useTheme
的组件都能拿到最新值,完全不需要一层层 props!
✨ 为什么推荐配合自定义 Hooks?
有人会问:
直接
useContext(ThemeContext)
不香吗?干嘛还要
useTheme
再包一层?
原因:
- 复用:以后换全局主题管理方式,只改
useTheme
,组件不动 - 习惯:大团队里常见,把所有上下文都封装成
useXXX
,一眼就知道干啥 - 组合:可以在
useTheme
里加其他副作用逻辑,比如主题持久化
useContext
还有哪些坑?
简单提醒
✅ Provider
必须在外层包住,用错位置会报 undefined
✅ Context
适合放全局状态,不要滥用(业务局部用 props 就行)
✅ 需要多状态共享?就多个 Context + Provider 嵌套
🪄 再来个套路总结
createContext
创建上下文对象Provider
全局声明可用范围 + 提供状态useContext
消费上下文里的状态- 自定义 Hook 封装
useContext
,解耦可复用
数据共享,肯定不止一种方法!
说到这,很多人会问:
"有了 Context,还要不要用 props?要不要用 Zustand / Redux?"
答案:
- 单向数据流 props 依旧最轻便,能 props 就 props
- Context 适合跨层级全局状态(用户信息、主题、语言等)
- Zustand、Redux 适合更复杂的状态(异步、状态中转、状态快照)
咱写 React,不是把啥都塞到 Context 就行,选对场景最重要。
✅ 写在最后
今天一口气学了:
useContext
怎么解决组件传参烦恼Provider
怎么声明- 自定义 Hook 怎么封装
ThemeContext
主题切换完整流程
结尾
看完有收获?点个赞 + 收藏吧!
评论区告诉我:
你平时还会用 Context
管理啥全局状态?