1️⃣ 开门见山:一句话定义
useContext
是 React 提供的一个 Hook,用于在函数组件中订阅 Context,并获取其当前值。它本质是读取最近<Provider>
提供的值,当 Provider 的 value 变化时,所有使用该 context 的组件都会重新渲染。
2️⃣ 核心原理分步说明(重点!)
🧩 ① Context 是什么?
- 通过
React.createContext(defaultValue)
创建。 - 返回一个对象,包含
Provider
、Consumer
,以及内部字段如_currentValue
。 - Provider 是一个组件,负责向下传递 value。
🔄 ② useContext 如何“读取”值?
- 在函数组件中调用
useContext(MyContext)
时:- React 会从当前组件对应的 Fiber 节点 开始,向上查找最近的 Provider。
- 获取该 Provider 的
value
(实际读取的是 Context 对象的_currentValue
)。 - 如果没有 Provider,则返回
defaultValue
。
⚠️ 注意:这不是“订阅/监听”,而是每次渲染时“主动读取”。
🚦 ③ 为什么 Provider 改变会触发子组件重渲染?
- 当
<Provider value={newValue}>
的 value 发生变化(Object.is
判断不相等):- React 会标记该 Provider 的所有后代组件中“使用了这个 context”的组件为“需要更新”。
- 通过 Fiber 节点上的
dependencies
字段记录 context 依赖。 - 在下次渲染时,这些组件会被重新执行。
✅ 这就是为什么 useContext 是“响应式”的 —— 它和 setState 一样,能触发组件更新。
3️⃣ 补充关键点(体现深度)
📉 性能问题(高频考点!)
- 没有选择性更新:哪怕你只用了
{ theme } = useContext(Context)
,只要 Context 的 value 变了,组件就会重渲染。 - 优化方案:
- 拆分 Context(如 ThemeContext、UserContext)
- 使用
useMemo
+ 自定义 Hook 包装 context 值 - 大型项目推荐用 Redux Toolkit 等
🚫 使用限制
- 必须在组件顶层调用(不能在条件/循环中),遵循 Hooks 规则。
- 不要滥用:Context 适合“全局、低频更新”的状态(如主题、语言、用户信息),不适合高频变化的状态(如表单输入)。
4️⃣ 举个简单例子(加分项)
const ThemeContext = React.createContext('light');
function App() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext); // ← 读取最近 Provider 的值
return <div className={theme}>Toolbar</div>;
}
当
theme
从'light'
变成'dark'
,Toolbar
会自动重渲染。
5️⃣ 总结升华(体现思考)
所以
useContext
的本质是 React Fiber 架构下的“依赖追踪 + 值读取”机制。它简化了跨层级传参,但要注意性能陷阱。在实际项目中,我通常会结合useReducer
做状态管理,或拆分多个 Context 避免不必要的渲染。
🎯 面试加分技巧
- ✅ 提到 Fiber:说明你知道 React 16+ 的底层架构。
- ✅ 提到 _currentValue:说明你看过源码或深入研究过。
- ✅ 强调“无订阅机制”:很多候选人误以为是发布订阅,纠正这一点很加分。
- ✅ 主动谈性能优化:体现工程思维,不只是会用,还会用好。
- ✅ 对比 Redux / Zustand:说明你理解适用场景。
🚫 避免的错误回答
- ❌ “useContext 是发布订阅模式” → ❌ 错!它没有事件监听。
- ❌ “useContext 会缓存值” → ❌ 错!每次渲染都重新读取。
- ❌ “只有值变了才渲染” → ✅ 对,但要说明是
Object.is
浅比较。