React Context 跨层传值
目标:会建 Provider +
useContext;知道什么时候不该用 Context。
引言
props 适合父子:父把值传给直接孩子。
跨很多层时(顶栏主题开关 → 深层 Panel),层层 props 叫 prop drilling,又吵又脆。
Context 做「依赖注入」:上面 Provider 放一份值,任意深层用 useContext 取,中间组件不用接力。
本篇 demo:主题 light/dark,Toolbar 切换,Panel 直接读主题变色。
动手
bash
cd react2/11-context/demo
npm install && npm run dev
点「切换」,看工具栏文案和面板底色一起变。
1. 三步:create → Provider → useContext
tsx
type Theme = 'light' | 'dark'
const ThemeCtx = createContext<{ theme: Theme; toggle: () => void } | null>(null)
function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>('light')
const toggle = () => setTheme((t) => (t === 'light' ? 'dark' : 'light'))
return (
<ThemeCtx.Provider value={{ theme, toggle }}>
{children}
</ThemeCtx.Provider>
)
}
function useTheme() {
const ctx = useContext(ThemeCtx)
if (!ctx) throw new Error('useTheme 必须在 ThemeProvider 内')
return ctx
}
| 步骤 | 干什么 |
|---|---|
createContext |
建上下文;默认值常用 null,配合自定义 hook 做「必须在 Provider 内」检查 |
Provider value={...} |
把当前值注入子树 |
useContext / useTheme |
消费;封装 hook 可读性更好 |
2. 深层组件直接消费
tsx
function Toolbar() {
const { theme, toggle } = useTheme()
return (
<div className="row">
<span>当前主题:{theme}</span>
<button type="button" onClick={toggle}>切换</button>
</div>
)
}
function Panel() {
const { theme } = useTheme()
return (
<section style={{ background: theme === 'dark' ? '#0f172a' : '#fff' }}>
<p>深层组件也能读到 Context,不必层层 props。</p>
</section>
)
}
export default function App() {
return (
<ThemeProvider>
<Toolbar />
<Panel />
</ThemeProvider>
)
}
App 只包一层 Provider;中间没有「theme 透传」。
3. 适合 / 不适合
| 适合 | 不适合 |
|---|---|
| 主题、locale、当前用户 | 每个按键都变的表单字段 |
| 依赖注入式配置 | 只传一两层就能搞定的数据 |
| 低频、横切关注点 | 高频大树更新(易牵动整棵消费者) |
高频客户端状态 → 拆多个 Context,或上 Zustand(15 篇)。
服务端列表 / 详情 → TanStack Query(07 篇),别硬塞 Context。
4. 常见坑
| 做法 | 问题 |
|---|---|
Provider 外用 useTheme |
拿到 null / 抛错;检查挂载位置 |
value={{ theme, toggle }} 每次新建对象 |
所有消费者可能跟着重渲染;可 useMemo 稳住 value,或拆成「状态 Context + 派发 Context」 |
| 什么都塞一个大 Context | 一处改、全家刷;按域拆分 |
| 用 Context 代替近距离 props | 过度设计,可读性变差 |
demo 在练什么
createContext+ Provider + 自定义useTheme- 跨组件读写主题,无 props 钻取
- 建立「低频横切用 Context」的选型直觉
自己改一改:再加一个 LocaleContext;或把 value 用 useMemo 包起来观察渲染。