React18新hook:useDeferredValue
useDeferredValue 的基础用法
useDeferredValue 可以让你延迟更新 UI 的某些部分
用法如下:
const deferredValue = useDeferredValue(someValue);
其中someValue是你想要延迟的值,它可以是任何类型。
deferredValue的渲染有两种情况:
- 在初始渲染时,
deferredValue的值将与someValue的值相同。 - 在UI更新期间,因为
deferredValue的优先级较低,即使并发模式下deferredValue已在后台更新,React也会先使用旧值渲染,当其它高优先级的状态更新完成,才会把deferredValue新值渲染出来。
useDeferredValue 应用示例
在新内容加载期间显示旧内容
比如,在搜索时,我们可能会配合 Suspense 组件,当发起搜索请求时,展示 fallback 的内容,请求完毕时,再显示最新的结果。
import { Suspense, useState } from 'react';
import SearchResults from './SearchResults.js';
export default function App() {
const [query, setQuery] = useState('');
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<SearchResults query={query} />
</Suspense>
</>
);
}
像这样,有一个 loading 效果:

然而,我们可以通过 useDeferredValue,在搜索时,延迟更新的结果,也就是在发送搜索请求时,不展示 loading,旧数据依然展示(下面的例子我们给旧数据置灰),当请求完毕时,再去渲染最新的数据
import { Suspense, useState, useDeferredValue } from 'react';
import SearchResults from './SearchResults.js';
export default function App() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<div style={{
opacity: isStale ? 0.5 : 1,
transition: isStale ? 'opacity 0.2s 0.2s linear' : 'opacity 0s 0s linear'
}}>
<SearchResults query={deferredQuery} />
</div>
</>
);
}
- 当搜索
a时:

- 然后再搜索
ab时,旧数据置灰,当请求成功时,再渲染最新的数据


延迟渲染 UI 的某些部分
你还可以将 useDeferredValue 作为性能优化的手段。当你的 UI 某个部分重新渲染很慢、没有简单的优化方法,同时你又希望避免它阻塞其他 UI 的渲染时,使用 useDeferredValue 很有帮助。
想象一下,你有一个文本框和一个组件(例如图表或长列表),在每次按键时都会重新渲染:
// app.jsx
import { useState, useDeferredValue } from 'react';
import SlowList from './SlowList.js';
export default function App() {
const [text, setText] = useState('');
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList text={text} />
</>
);
}
每次你输入的时候,SlowList 都会接受到新的 props,然后重新渲染,就会导致卡顿,然而,我们可以通过 useDeferredValue 去做延迟渲染
// app.jsx
import { useState, useDeferredValue } from 'react';
import SlowList from './SlowList.js';
export default function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList text={deferredText} />
</>
);
}
// SlowList.jsx
import { memo } from 'react';
const SlowList = memo(function SlowList({ text }) {
// 仅打印一次。实际的减速是在 SlowItem 组件内部。
console.log('[ARTIFICIALLY SLOW] Rendering 250 <SlowItem />');
let items = [];
for (let i = 0; i < 250; i++) {
items.push(<SlowItem key={i} text={text} />);
}
return (
<ul className="items">
{items}
</ul>
);
});
function SlowItem({ text }) {
let startTime = performance.now();
while (performance.now() - startTime < 1) {
// 每个 item 暂停 1ms,模拟极其缓慢的代码
}
return (
<li className="item">
Text: {text}
</li>
)
}
export default SlowList;
效果如下:
-
在输入停止前,不会 rerender

-
输入停止后,再渲染

useDeferredValue 和 节流、防抖的区别
从上面的学习可以看出来,useDeferredValue在某些场景下是可以替换掉节流和防抖的
防抖与节流的局限性:
- 这两种方法都是为了
控制函数的执行频率,但它们是阻塞的,可能会导致不流畅的用户体验。
useDeferredValue的优势:
- 它与React深度集成,可以适应用户的设备。如果设备性能好,延迟的重新渲染会很快完成;如果设备性能差,重新渲染会相应地延迟。
- 它不需要选择固定的延迟,与防抖和节流不同。
- 由
useDeferredValue执行的重新渲染是可中断的。这意味着在React重新渲染期间,如果发生了其他更新,React会中断当前的渲染并处理新的更新。
适用场景:
- 如果要优化的工作不是在渲染期间进行的,例如减少网络请求,那么防抖和节流仍然是有用的。
- 如果优化的目标是和渲染有关的,建议使用
useDeferredValue
转载自:https://juejin.cn/post/7280778306584608768