跳转到主要内容

Context 深入

Context 很强大:它能让你“跨层级”传值,避免 props 一层层往下传。但它也很容易被滥用,并且会影响渲染范围。 这一章专门讲 Context 的进阶用法与性能注意事项。

复习:最小 Context

TypeScript
import { createContext, useContext } from 'react';

type Theme = 'light' | 'dark';
const ThemeContext = createContext<Theme>('light');

function Button() {
  const theme = useContext(ThemeContext);
  return <button className={theme === 'dark' ? 'dark' : 'light'}>按钮</button>;
}

export default function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Button />
    </ThemeContext.Provider>
  );
}

性能关键点:Provider value 的引用稳定性

如果你把一个新对象直接作为 value 传入,那么每次渲染都会创建新引用,导致订阅者频繁渲染。

TypeScript
import { createContext, useContext, useState } from 'react';

type Auth = { userId: string | null; login: () => void };
const AuthContext = createContext<Auth | null>(null);

function Profile() {
  const auth = useContext(AuthContext);
  return <p>userId: {auth?.userId ?? 'anonymous'}</p>;
}

export default function App() {
  const [userId, setUserId] = useState<string | null>(null);

  const value = {
    userId,
    login() {
      setUserId('u_123');
    },
  };

  return (
    <AuthContext.Provider value={value}>
      <Profile />
    </AuthContext.Provider>
  );
}

更好的做法是稳定函数引用,并用 useMemo 缓存 value:

TypeScript
import { createContext, useContext, useMemo, useState, useCallback } from 'react';

type Auth = { userId: string | null; login: () => void };
const AuthContext = createContext<Auth | null>(null);

function Profile() {
  const auth = useContext(AuthContext);
  return <p>userId: {auth?.userId ?? 'anonymous'}</p>;
}

export default function App() {
  const [userId, setUserId] = useState<string | null>(null);

  const login = useCallback(() => setUserId('u_123'), []);

  const value = useMemo(() => ({ userId, login }), [userId, login]);

  return (
    <AuthContext.Provider value={value}>
      <Profile />
    </AuthContext.Provider>
  );
}

拆分 Context:降低重渲染范围

经验法则
  • 一个 Context 里放太多字段,任何一个字段变化都会影响所有订阅者。
  • 把“经常变的”和“很少变的”拆成多个 Context,能显著减少渲染波及。

什么时候不要用 Context?

  • 只是父传子两三层:先尝试拆组件/提升 state/组合 children。
  • 把组件内部的局部状态硬塞到 Context:会让依赖关系变得隐蔽。
  • 需要复杂的派生、选择器、时间旅行:可能需要专用状态管理方案。

下一步

动手练习

运行并修改下面的组件,熟悉交互式示例的编辑与预览。

🌐浏览器运行
Context 深入 - React 文档 - React 文档