先记住这个答案
Context 的 value 变化会影响消费它的组件,因此把互不相关的数据装进同一个值,可能扩大不必要的读取与计算。拆分时先看哪些组件真正需要哪些数据,再看更新是否同步、范围是否一致。常见边界有主题与编辑状态、状态与命令、页面资源与全局配置。拆分应保留清晰的数据所有权,不能为了减少渲染把一份权威状态复制成多份。
- 按消费者依赖和业务范围确定拆分边界
- 高频变化与低频配置混在一起时值得测量
- 多个 Context 可以从同一个状态所有者提供值
从消费者矩阵找到独立的数据群
假设文档编辑器的 Context 同时提供主题、只读设置、选区信息和保存命令。导航只需要主题,编辑区域需要选区,工具栏要读只读设置并发送保存命令。先列清各组件实际使用的字段,就能发现部分数据具有独立消费者,而不必立即为每个字段创建一个 Context。
如果两个字段总在同一个业务动作中变化,而且所有消费者都同时读取它们,拆成两个通道未必有明显收益。相反,即使只有两个字段,只要其中一个频繁改变、另一个控制昂贵区域,拆分也可能值得。频率没有固定数量级门槛,应结合每次更新的实际工作量判断。
拆分通道不等于拆散状态约束
选中的文档 ID 与该文档的可编辑权限可能需要一起更新,可以继续由同一个父层状态或 reducer 计算,再分别提供给不同消费范围。这样既减少部分无关订阅,也保留一次业务转移的统一来源。不要拆完再用两个 Effect 将相互依赖的状态来回同步。
提供者怎样嵌套取决于谁需要同时读取它们。覆盖同一片后代的两个 Context 可以嵌套;只属于侧栏的数据可以放在侧栏区域。不能把本来共同需要的提供者机械改成互不包含的并列分支,然后期待同一消费者能越过树结构读取另一支的值。
检查引用和父层渲染,确认实际收益
拆分后直接传递字符串、稳定的 dispatch 或未变化的 state 引用,就已经可能有效,不要求每个 value 都包 useMemo。若值是每次新建的对象,才需要分析这些字段是否真的变了,以及缓存对象是否能减少通知。useMemo 依赖也必须覆盖构造该值所读取的输入。
验收可围绕一个明确操作展开,例如移动选区时检查主题预览是否还执行昂贵计算,再单独切换主题确认它确实更新。若仍有重复工作,应继续看父组件渲染与 props 身份。只统计 Provider 数量或声称拆完就让整个页面零渲染,都不能证明用户交互改善。
容易答错的地方
- 每个字段一个 Context 就是最细粒度的最佳方案
- 过度拆分会让消费者依赖大量通道,增加挂载与测试成本,却未必减少必要计算。优先表达稳定的业务边界,并用实际更新场景检验收益。
- Context 太多只能换一个全局大对象
- 可以用一个组合 Provider 封装挂载结构,同时保留内部清晰的通道;也可以按页面范围放置。减少调用方的样板代码,不必以重新混合所有数据为代价。
面试官还会怎么问?
一个组件读取多个 Context 是坏设计吗?
不是,组件确实跨越多个数据域时这样做很自然。只有大量组件总是共同读取相同的一组值时,才值得重新检查拆分是否符合实际依赖。
频繁更新的数据必须换状态库吗?
不一定,先判断它能否留在局部状态,或缩小消费范围。如果确实需要大量组件按不同片段订阅外部状态,再评估具有选择器和一致快照协议的方案。
拆分后还需要 memo 吗?
按需决定。拆分减少 Context 触发的无关工作,memo 可以帮助处理 props 未变时的父层渲染;两者解决的问题有关联但不相同,不能用一个固定组合替代测量。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。