React 18引入了一种新的钩子useDeferredValue
,可以通过延迟UI的某些部分的更新来提高应用程序的性能和用户体验。useDeferredValue
钩子允许你延迟更新UI的一部分,直到下一个渲染周期。让我们探讨一下useDeferredValue
的作用,如何使用它以及一些使用案例。
内容
- 什么是useDeferredValue?
- 为什么使用useDeferredValue?
- 何时使用useDeferredValue?
- useDeferredValue的最佳实践
- 结论
- 了解更多
什么是useDeferredValue?
useDeferredValue
是一个React钩子,它接受一个值并返回一个新的值副本,该副本将延迟到更紧急的更新。延迟的值在初始渲染期间与原始值相同,但在后续渲染中会有延迟更新。这种延迟允许React优先处理其他更重要的更新,例如打字或滚动。
例如,如果你有一个输入字段和一个依赖于输入值的项目列表,你可以使用useDeferredValue
来延迟更新列表,直到输入值稳定。这样,你可以避免不必要的重新渲染,并保持输入字段的响应性。
useDeferredValue
的语法非常简单:
const deferredValue = useDeferredValue(value);
value
参数可以是任何类型的值,例如字符串、数字、对象或数组。钩子将返回一个延迟版本的值,当React优先处理更重要的更新时,该值将在后台更新。
为什么使用useDeferredValue?
useDeferredValue
的主要用例是在加载新内容时显示旧内容。这可以帮助避免不必要的重新渲染和不流畅的UI过渡,从而提高应用程序的性能和用户体验。
例如,假设你有一个搜索功能,允许用户过滤一个大型项目列表。过滤大型列表可能会消耗大量计算资源并需要很长时间才能完成。如果你在每次按键时更新UI,你将导致频繁的重新渲染,这会使输入变得不流畅和不响应。此外,你将显示与用户查询不相关的中间结果。
function SearchPage() {
const [query, setQuery] = useState("");
const list = useMemo(() => {
// Filtering through large list impacts performance
return largeList.filter((item) => item.name.includes(query));
}, [query]);
const handleChange = (event) => {
setQuery(event.target.value);
};
return (
<>
<input
type="text"
value={query}
onChange={handleChange}
placeholder="Search"
/>
{list.map((item) => (
<SearchResultItem key={item.id} item={item} />
))}
</>
);
}
我们有一个状态变量query
,用于存储输入值,以及一个记忆化变量list
,根据查询筛选一个大型项目数组。这段代码的问题在于,每次用户在输入字段中输入时,React都会使用更新后的查询和列表重新渲染组件。这可能会导致性能问题并使输入字段变得不流畅。
更好的方法是使用useDeferredValue
来延迟更新列表,直到用户停止输入。这样,你可以在新列表在后台计算时显示先前的列表。你还可以通过添加一些视觉提示(例如旋转器或淡入效果)来表示列表已过时。
function SearchPage() {
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);
// Filter through a large list based on the deferred query
const list = useMemo(() => {
return largeList.filter((item) => item.name.includes(deferredQuery));
}, [deferredQuery]);
// Indicate that the list is stale if it doesn't match the query
const isStale = deferredQuery !== query;
const handleChange = (event) => {
setQuery(event.target.value);
};
return (
<>
<input
type="text"
value={query}
onChange={handleChange}
placeholder="Search"
/>
{isStale && <Spinner />}
{list.map((item) => (
<SearchResultItem key={item.id} item={item} isStale={isStale} />
))}
</>
);
}
现在,当用户在输入字段中输入内容时,React将首先尝试使用旧查询和列表重新渲染组件(因此它将返回旧值),然后在后台尝试使用新查询和列表重新渲染组件(因此它将返回更新的值)。这样,我们就可以避免阻塞UI线程并保持输入字段的响应性。
何时使用useDeferredValue?
在你的UI的某些部分依赖于频繁更改的值,并导致昂贵的计算或网络请求的情况下,useDeferredValue
可能会很有用。例如:
- 显示过时内容,同时加载新内容。你可以使用
useDeferredValue
在从API获取新版本的某些数据时显示旧版本的数据。这可以防止显示加载旋转器或回退,并使你的应用程序感觉更快。 - 指示内容已过时。你可以使用
useDeferredValue
来显示一些视觉提示,指示某些数据已过时并需要刷新。例如,你可以显示一个灰色文本或一个图标,表示从延迟值派生的某些内容。 - 延迟重新渲染UI的一部分。你可以使用
useDeferredValue
来延迟更新用户不重要或不可见的UI的某些部分。例如,你可以延迟更新某些隐藏在选项卡或手风琴后面的图表或图形。
useDeferredValue的最佳实践
在使用useDeferredValue
时需要注意一些注意事项和最佳实践。
- 传递给
useDeferredValue
的值应该是原始值(例如字符串和数字)或在渲染之外创建的对象。如果你在渲染期间创建一个新对象并立即将其传递给useDeferredValue
,它将在每次重新渲染时都不同,导致不必要的后台重新渲染。 useDeferredValue
本身并不会防止额外的网络请求。你仍然应该根据用户输入进行防抖或节流,并在不再相关时取消任何待处理的请求。useDeferredValue
本身没有固定的延迟。一旦React完成原始重新渲染,React将立即开始处理具有新延迟值的后台重新渲染。任何由事件(例如打字)引起的更新都将中断后台重新渲染并优先处理它们。useDeferredValue
引起的后台重新渲染直到提交到屏幕时才会触发效果。如果后台重新渲染挂起,则它的效果将在数据加载并UI更新后运行。useDeferredValue
与<Suspense>
集成。如果由新值引起的后台更新挂起UI,则用户将看不到回退。他们将看到旧的延迟值,直到数据加载。# 结论
useDeferredValue
是一个强大的钩子,可以通过延迟更新UI的一部分来帮助你优化React性能。它可以在新内容加载时显示旧内容,避免不流畅的UI过渡,从而改善用户体验。但是,你也应该了解它的注意事项和最佳实践,以正确使用它。
了解更多
使用JavaScript构建GPT、LangChain的个人助理
译自:https://itnext.io/best-practices-react-usedeferredvalue-to-improve-performance-7b67a08940b9
评论(0)