先记住这个答案
props 让组件输入显式可见,适合边界清晰、容易直接传递的数据。Context 适合同一子树中多个位置共享的配置或资源,可以减少无关中间层代传参数。但它会把依赖转移到提供者范围与消费 Hook 中,增加孤立复用和测试时的挂载要求。选择前也应考虑组件组合:由掌握数据的父层构造内容,再通过 children 交给布局,可能已经消除冗余传递。
- props 显式展示输入,Context 提供范围内共享依赖
- 中间层只搬运参数是信号,不是固定层数门槛
- 先看能否通过组件组合减少传递,再决定是否建立通道
先判断参数是在表达依赖还是单纯被转交
一个商品卡片接收 price 和 currency,输入直接决定展示结果,这是清楚的 props 边界。若页面布局、面板和容器都只把 locale 原样转交,真正使用它的是多个深层日期与金额组件,就可以评估在这个业务范围建立地区设置 Context。
但不能仅凭经过三层组件就机械替换。父层若能直接创建带好参数的金额内容,再通过 children 交给布局,中间组件就无需认识 currency。组件组合保留了依赖的显式构造,也不会额外要求内容只能在某个 Provider 中工作,常适合通用布局与插槽式界面。
局部地区设置怎样减少搬运参数
下面示例让 RegionContext 提供一个地区字符串,深层税率说明读取它,布局组件只负责排版。Provider 位于页面范围,另一个独立页面可以使用不同的地区设置。这里使用默认地区是有意的产品约定;若业务要求调用方必须配置,则应采用空标记并在消费 Hook 中检查。
这类 Context 适合共享环境配置,但并不要求把每个子组件的所有 props 都移进去。金额、商品 ID 和按钮动作仍可保持显式输入。测试税率说明时可以直接包上相应 Provider,分别验证不同地区;同时测试没有提供者时是否符合约定,避免默认值悄悄掩盖错误挂载。
import { createContext, useContext } from 'react';
const RegionContext = createContext('CN');
function RegionNotice() {
const region = useContext(RegionContext);
return <p>当前地区:{region},结算规则以订单确认为准。</p>;
}
function CheckoutPanel() {
return <section aria-label="结算信息"><RegionNotice /></section>;
}
export default function CheckoutPage() {
return (
<RegionContext.Provider value="SG">
<CheckoutPanel />
</RegionContext.Provider>
);
}此组件展示 SG 地区提示,CheckoutPanel 无需接收再转交 region。示例只演示数据传递,不计算或承诺任何具体税务规则,也没有控制台输出。
把消费频率和复用成本一起考虑
高频输入若只有一个编辑器使用,放在本地状态通常最直接。将它放进一个还包含全站主题的大 Context,再用 useMemo 包装,仍会在输入实际改变时更新整个 value。需要减少无关消费者工作时,应检查范围与依赖,而不是期待缓存忽略真实变化。
Context 也会影响组件离开原页面后的复用方式。通用展示组件可以继续接受必要 props,由业务容器负责读取 Context;这样既保留业务层共享,也让展示组件容易在测试、预览和其它页面中使用。是否采用这种分层取决于复用需求,不必把每个简单组件都拆成两层。
容易答错的地方
- 用了 Context 就应该去掉所有 props
- Context 适合某个范围内的共享依赖,不会使显式输入失去价值。把局部参数也隐藏进去,会让调用处难以看出组件行为,增加测试和复用的成本。
- Context 只要嵌套够深就比 props 快
- Context 首先解决传递与组织问题,并没有按层数保证性能。更新值的身份、消费者数量和工作成本都影响结果,应先保证结构清楚再测量实际交互。
面试官还会怎么问?
props drilling 一定是坏代码吗?
不是,少量显式传递可以清楚表达数据流。只有中间层持续搬运与自身职责无关的参数,且影响维护时,才值得考虑组合或 Context。
组件没有调用 useContext 就不会重渲染吗?
它不会因为消费这条 Context 而收到更新,但仍可能随父层渲染、props 或自己的 state 变化而执行。诊断时必须区分这些来源。
应用语言、主题和当前输入应放在一个 Context 吗?
它们通常具有不同生命周期和消费者范围,不能仅因为都要共享就合并。语言与主题也未必必须拆开,应结合实际消费关系,而当前局部输入通常先考虑留在局部。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。