useDeferredValue 的作用是什么?和防抖有什么区别?
简化版
useDeferredValue 会返回一个可延迟更新的值,让高优先级 UI 先更新,依赖延迟值的重组件稍后更新。它不是防抖,不按固定时间等待,也不会减少输入变化本身,而是把慢渲染放到较低优先级。
详细版
常见场景是输入框使用实时值,大列表使用延迟值。
const [text, setText] = useState('')
const deferredText = useDeferredValue(text)
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<BigList keyword={deferredText} />
</>
)
输入框会尽快响应 text,BigList 根据 deferredText 稍后更新。防抖是时间策略,例如 300ms 后触发;useDeferredValue 是调度策略,由 React 根据渲染压力安排。
完整版教学
一、它解决的是派生重 UI 拖慢输入
有些页面一个状态会同时驱动轻 UI 和重 UI。 输入框显示字符很轻,但下面的大列表过滤和渲染很重。 如果二者使用同一个实时值,输入每变一次都可能带着大列表一起更新。
text 变化
├─ input value 更新 轻
└─ BigList 过滤和渲染 重
当 BigList 有 5000 行时,输入一个字符就触发大量渲染。
useDeferredValue 让 BigList 使用“稍慢一拍”的值,使输入框先保持流畅。
二、deferred value 是旧值暂时可用
useDeferredValue(text) 返回的值可能暂时落后于 text。
React 会在紧急更新完成后,再安排依赖 deferred 值的渲染。
这意味着 UI 中可能同时存在新输入和旧结果。
const deferredQuery = useDeferredValue(query)
const isStale = query !== deferredQuery
用户输入 react
query: react
deferredQuery: rea
列表仍显示 rea 的结果
稍后 deferredQuery 追上 react
可以用 isStale 给列表加透明度或提示。
这样用户知道结果正在追赶输入。
三、它和 useTransition 的角度不同
useTransition 是包住某次状态更新,把更新标记为非紧急。
useDeferredValue 是拿一个值的延迟版本,让使用该值的子树降低更新紧急度。
一个偏“更新动作”,一个偏“值的消费”。
| 对比 | useTransition | useDeferredValue |
|---|---|---|
| 作用对象 | 状态更新 | 某个值 |
| 是否有 pending | 有 | 没有直接 pending |
| 常见位置 | 事件处理里 | 渲染逻辑里 |
| 典型场景 | 手动标记慢更新 | 子组件消费延迟值 |
如果你能控制 setState 的地方,用 transition 很直接。 如果值来自 props 或上层,当前组件不能控制更新来源,deferred value 更方便。
四、它不是防抖,因为没有固定等待时间
防抖常写成“输入停止 300ms 后再执行”。
useDeferredValue 不承诺等待固定毫秒数。
React 会根据当前渲染压力安排延迟更新。
防抖:
固定 300ms 等待
useDeferredValue:
输入先更新
重 UI 在较空闲或较低优先级时追上
如果业务要求“用户停下 500ms 才请求接口”,应该用防抖。
如果只是希望输入不卡,而结果可以稍后渲染,用 useDeferredValue。
五、它仍需要配合 memo 才能发挥效果
如果重组件没有 memo,父组件渲染时它仍可能跟着执行。
通常会把依赖 deferred 值的重组件拆出去,并用 memo 降低无关重渲染。
const BigList = memo(function BigList({ keyword }) {
// 重过滤和重渲染
})
假设父组件因为输入更新渲染 10 次。 如果 BigList 每次都跟着重算,deferred 的价值会被削弱。 拆分组件、memo、虚拟列表和 deferred value 经常要配合使用。
六、不要用于必须强一致的显示
有些 UI 必须和输入值完全同步,例如密码强度提示、表单校验错误、金额计算。 如果延迟显示,用户可能误解当前状态。 这类内容不适合使用 deferred value。
记忆钩子:deferred value 是“慢镜头副本”,输入用新值,重内容用慢一拍的值。
这个比喻能帮助区分它和防抖。 防抖是等用户停,deferred 是让慢 UI 追。
七、常见误区与追问
- 误区:useDeferredValue 是防抖 Hook。 它不按固定时间等待,而是交给 React 调度较低优先级更新。
- 误区:所有值都可以延迟。 强一致展示、表单校验和关键反馈不适合延迟。
- 误区:用了 deferred 就不需要优化列表。 大列表仍要配合 memo、虚拟列表或减少计算量。
- 追问:如何判断列表是否落后? 可以比较原值和 deferred 值,例如
query !== deferredQuery。 - 追问:它和 useTransition 最大区别是什么? transition 标记更新,deferred value 延迟消费值。
- 追问:值来自父组件怎么办? 当前组件无法控制父级 setState 时,使用
useDeferredValue很合适。
八、加强记忆
useDeferredValue 记成“延迟值副本”。输入框看实时值,大列表看延迟值;它解决交互响应,不替代防抖请求;要配合组件拆分、memo 和虚拟列表,别指望一个 Hook 把所有重活变轻。