延迟更新与交互响应
延迟更新与交互响应 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
读完你能在「输入必须跟手、结果可以慢一点」的场景里使用 useDeferredValue 和 useTransition,说清这两个 API 的分工,并用快速输入验证:输入框始终即时响应,昂贵的结果晚一拍更新,界面不再被计算卡住。
问题场景
课程库搜索:输入框每敲一个字,都对两万多条记录做一遍过滤。键盘越敲越卡——因为每次按键都要等最贵的部分算完才肯更新界面,连输入框自己的回显都被拖累了。用户真正要的是一个优先级安排:字符出现在输入框里是高优先级,列表结果更新是低优先级。
核心解释
React 把更新分成紧急与非紧急两档,两个 API 分别覆盖两种写法。写法一:值在渲染中消费,用 useDeferredValue:
const deferredKeyword = useDeferredValue(keyword);
const result = useMemo(() => heavyFilter(deferredKeyword), [deferredKeyword]);
// 输入框绑定 keyword,立刻更新;列表绑定 deferredKeyword,允许晚一拍
写法二:更新在事件处理函数里发起,用 useTransition:
const [isPending, startTransition] = useTransition();
function onTabChange(next) {
setKeyword(e.target.value); // 紧急:输入反馈立刻渲染
startTransition(() => setActive(next)); // 非紧急:切换大列表可以被插队
}
共同点:它们都不让计算变快,而是让贵的更新给紧急更新让路。输入帧永远优先渲染,结果帧慢一点,用户反而觉得更跟手。分工看视角:useDeferredValue 管「一个值被消费的时刻」,适合受控输入加派生计算;useTransition 管「一段更新的发起时机」,适合事件里切 tab、翻页这类成片的非紧急更新。
验证方式:页面同时显示「输入框的值」和「列表正在用的值」。快速连续输入时,开启延迟的两个值会短暂不一致——这正是延迟生效的直接证据;关闭延迟则每一拍都要等计算完成,输入明显发滞。
示例拆解
实验区是一个一万两千条记录的搜索页:
- 「使用 useDeferredValue」开关:关 = 输入直接驱动列表;开 = 延迟值桥接;
- 中间实时对比「输入框的值」与「列表正在用的值」,快速输入时能看到差拍提示;
- 「切换到大库」按钮用 useTransition 包住:切换三万六千条数据期间按钮显示非紧急提示,期间仍可正常打字;
- 验证:关闭延迟快速输入 5 个字符,感受卡顿;打开后再输一遍,对比手感与两个值的差拍。
动手任务
打开本课实验区:
- 关闭延迟开关,快速输入一串字符,记录卡顿感;打开开关重复一遍,对比并留意两个值的差拍。
- 把
useDeferredValue(keyword)换成直接用 keyword(即删掉延迟一行),确认卡顿回归,再恢复。 - 连点「切换到大库」两次,观察 isPending 提示与最终落点:被打断的过渡更新不会留下中间结果,最终显示最后一次切换。
常见错误
- 把 useDeferredValue 当防抖:防抖是少执行,延迟更新是错峰执行;它不减少计算量,只是不让计算阻塞输入帧。
- 在延迟计算里做副作用:延迟渲染可能被中断重跑,副作用会执行多次;副作用应放在 Effect 或事件处理里。
- 把紧急更新标成 transition:把输入框自己的 setKeyword 包进 startTransition,输入反馈反而变慢;紧急的永远是用户直接操作的即时反馈。
- 期望它解决「单次计算太贵」:优先级重排不缩短单次耗时,配合上一课的缓存或后续的防抖请求才是完整方案。
检查与复盘
自查清单:
- 能说出分工:值消费时机用 useDeferredValue,更新发起时机用 useTransition
- 验证过:开延迟后快速输入,输入框回显与列表值出现差拍,且输入不卡
- 知道 isPending 表示有非紧急更新在排队,不代表紧急更新被延迟
参考资料
- React 官方文档:useDeferredValue —— 值延迟的语义与官方输入框示例
- React 官方文档:useTransition —— 标记非紧急更新与 isPending 的约定
读完这一课了?
标记完成后,工作台会安排接下来的学习。