Context 共享状态
createContext、Provider、跨层传值。
Context 共享状态
这一课你将能做到
- 说出 Context 解决「props 一层层往下传」的麻烦
- 看懂 Provider + useContext 的基本结构
- 知道不要把所有数据都塞进 Context
为什么要学
主题色、当前用户、走法偏好——很多组件都要读。
若用 props 层层传递(prop drilling),中间层只为「转发」而存在,又烦又脆:Header 并不关心 user 怎么来的,却必须写 user={user} 往下递。
Context 提供「在树上的某处放一份值,深处直接读」。
人话:广播站。上面架天线(Provider),下面收音机(useContext)。
本站 AuthProvider 就是这类模式:登录态不必穿过每一层布局。
先建立图像
<AuthProvider> ← 提供 { user, setUser }
<Page>
<Header /> ← useContext 读取 user
<Main /> ← 不需要 user 的层不用接 props
</Page>
</AuthProvider>
没有 Provider 就调用 useContext,会拿到 createContext 时的默认值(常为 null),于是你要自己报错提醒。
跟着做
步骤 1:最小 Context(完整可抄)
import { createContext, useContext, useMemo, useState } from "react";
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const value = useMemo(() => ({ user, setUser }), [user]);
return (
<AuthContext.Provider value={value}>{children}</AuthContext.Provider>
);
}
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useAuth 必须在 AuthProvider 内");
return ctx;
}
export function WhoAmI() {
const { user } = useAuth();
return <span>{user ? user.name : "未登录"}</span>;
}
export function LoginButton() {
const { setUser } = useAuth();
return (
<button type="button" onClick={() => setUser({ name: "ada" })}>
演示登录
</button>
);
}
useMemo 避免每次渲染都新建 value 对象,导致所有消费者无谓更新。入门先知道「对象当 value 要小心」。
步骤 2:在树顶包起来
export function App() {
return (
<AuthProvider>
<WhoAmI />
<LoginButton />
</AuthProvider>
);
}
WhoAmI 不必从 App 接 user props。
步骤 3:先提升,不够再 Context
两个兄弟要同一份勾选:父组件 state 往往更简单,别一上来 Context。
跨很多层、很多页都要读的「当前用户 / 主题」再 Context。
步骤 4:拆分「值」与「dispatch」
高频更新的 value 与稳定的 setter 可分两个 Context,减少无关重渲染。入门先做一个 Provider 也行,知道有这招。