题目重点:解释可中断渲染、紧急输入与非紧急结果更新,并区分 transition、deferred value 和 debounce。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 以 React 18 客户端应用的慢搜索结果列表为训练场景;输入框必须及时响应,过滤视图可以稍后出现。
- 题目说的 Concurrent Mode 是口语化称呼;React 18 的并发能力通过相关 API 选择性启用,不能推断整个应用所有更新自动可中断。
解题路线
- 先分优先级:输入文本的受控状态属于紧急更新;依赖它的昂贵列表呈现可延后。`useTransition` 的 `startTransition` 标记其中同步触发的 state 更新为非紧急,`isPending` 可表示等待状态。
- 若组件只接收已变化的 query prop,可用 `useDeferredValue(query)` 让依赖视图暂时使用旧 query,再在后台准备新结果;旧值显示时可降低其视觉强调。
- 渲染工作可以被新的紧急更新打断,未完成的 render 不会作为中间结果 commit;但已经发生的网络请求、事件处理副作用并不会因此自动取消。
- 这两个 API 调整调度,不减少一次昂贵计算本身,也不按固定毫秒合并输入。若要减少请求,另用 debounce;若要降低单次计算成本,做索引、分页或列表虚拟化,并用 Profiler 比较交互延迟。
手动走一遍
用户先输入 a,列表开始计算;随即输入 ab。输入框应立即显示 ab,旧列表可短暂保留,React 可放弃尚未提交的 a 结果并准备 ab。若过滤还会请求服务器,必须另用取消/版本号防止 a 的晚到响应覆盖 ab。
规模与取舍
调度不会把过滤 N 条记录的 O(N) 工作变为 O(1),旧视图与新视图准备期间还可能增加瞬时内存。大列表优先量化输入延迟、commit 时间和请求数,再决定 transition、缓存与虚拟滚动组合。
易错点与追问
容易失分的边界
- 把并发渲染说成浏览器多线程并行执行组件代码。
- 认为 React 18 一上线所有更新都自动是 transition。
- 把 useDeferredValue 当固定 300ms debounce,从而误报网络请求会减少。
- 在 render 中执行副作用,忽略可重试或被丢弃的 render。
- 认为 transition 能保证服务器旧响应不会覆盖新响应。
继续深挖
- 受控输入的 state 本身为什么不应包在 transition 里?
- 慢组件仍每次重算时如何用测量区分调度收益与计算优化?
- Suspense 与 transition 一起使用时,旧内容和 pending 指示怎样协调?
- 异步请求如何用序号或 AbortController 防止乱序?
技术参考
- React 18 官方发布说明:说明并发渲染与 opt-in API。
- React useTransition:非紧急更新及 pending 语义。
- React useDeferredValue:延后视图更新,不是固定时间 debounce。
