跳到主要内容

useContext

面试速答(30 秒版 TL;DR)

  • useContext 用来读取最近一层 Context.Provider 提供的值,解决的是跨层级传参问题。
  • 它适合放:主题、语言、登录态、表单上下文、组件库内部上下文。
  • 不是状态管理库的完全替代品,因为只解决“传递”问题,不自动解决复杂更新组织、派生、缓存和调试能力。
  • React 18 下要特别注意:Provider value 变化会让消费它的组件重新渲染。

1. 心智模型

不用 Context 时,数据往往只能一层层透传:

  • 父传子
  • 子再传孙

这就是常说的 props drilling

useContext 的作用不是让数据“变成全局”,而是提供一条跨层传递通道

2. 基本用法

const ThemeContext = React.createContext<'light' | 'dark'>('light')

function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
)
}

function Toolbar() {
const theme = useContext(ThemeContext)
return <div>{theme}</div>
}

核心规则:

  • 就近读取最近的 Provider
  • 找不到时返回 createContext(defaultValue) 里的默认值

3. 它适合什么,不适合什么

3.1 适合

  • 主题 theme
  • 国际化 locale
  • 认证信息 auth
  • 组件库内部上下文
  • 表单或路由局部上下文

3.2 不适合直接承接所有复杂全局状态

因为一旦 Provider 的 value 变化,消费它的组件就可能重新渲染。

如果你把:

  • 大对象
  • 高频变化状态
  • 很多无关字段

都塞进一个 Context,更新范围就容易变大。

4. React 18 下的性能关注点

4.1 value 引用稳定

下面这种写法每次 render 都会创建新对象:

<AuthContext.Provider value={{user, logout}}>

更稳的做法通常是结合 useMemo / useCallback 控制引用稳定。

4.2 拆 Context

不要把完全不同的关注点塞进一个 Context。

例如可以拆成:

  • AuthStateContext
  • AuthActionContext

这样比一个大而全的对象更容易控制更新范围。

5. 面试高频答法

Q1:useContext 和 Redux / Zustand 有什么区别?

  • useContext 主要解决跨层传值
  • Redux / Zustand 这类库还解决:
    • 状态组织
    • 派生与订阅
    • 中间件
    • DevTools
    • 更新控制

所以 Context 更像“通道”,不是完整状态管理方案。

Q2:Context 会导致性能问题吗?

会,但不是“用了就慢”,而是:

  • Provider value 改变
  • 又没有合理拆分和稳定引用

这时消费组件更新范围可能被放大。

Q3:defaultValue 有什么用?

主要用于:

  • 没有 Provider 时的兜底值
  • 测试或演示场景的默认行为

但业务里一般仍建议明确包 Provider。

常见追问

1)可以在条件语句里调用 useContext 吗?

不可以。它仍然受 Hooks 调用顺序规则约束。

2)Context 是全局单例吗?

不是。它是按组件树作用域传播的。

易错点/坑

  • 把 Context 当成“任何场景都适合的全局状态管理”。
  • Provider value 每次都创建新对象,导致不必要更新。
  • 一个 Context 塞太多字段,更新扩散过大。

速记要点(可背诵)

  • useContext 解决的是跨层传值,不是完整状态管理。
  • React 18 下要关注 Provider value 的引用稳定和 Context 拆分。
  • 适合主题、语言、认证、组件库内部上下文。