React Query 会自动应用一些优化,以确保您的组件仅在实际需要时才重新渲染。这是通过以下方式完成的:
React Query 使用一种称为“结构共享”的技术来确保在重新渲染之间尽可能多地保留引用。如果通过网络获取数据,通常会通过 json 解析响应来获得一个全新的引用。但是,如果数据中_没有任何_更改,React Query 将保留原始引用。如果子集发生更改,React Query 将保留未更改的部分,并仅替换已更改的部分。
注意:此优化仅适用于 queryFn 返回与 JSON 兼容的数据的情况。您可以通过全局或在每个查询的基础上设置 structuralSharing: false 来关闭它,或者您可以通过向其传递一个函数来实现您自己的结构共享。
从 useQuery、useInfiniteQuery、useMutation 返回的顶层对象以及从 useQueries 返回的数组不是引用稳定的。每次渲染时它都会是一个新的引用。但是,从这些钩子返回的 data 属性将尽可能稳定。
React Query 仅在从 useQuery 返回的某个属性实际“使用”时才触发重新渲染。这是通过使用 Proxy 对象完成的。这避免了许多不必要的重新渲染,例如,因为像 isFetching 或 isStale 这样的属性可能会经常更改,但在组件中并未使用。
您可以通过全局或在每个查询的基础上设置 notifyOnChangeProps 来手动自定义此功能。如果您想关闭该功能,可以将 notifyOnChangeProps: 'all'。
注意:代理的 get 陷阱是通过访问属性来调用的,无论是通过解构还是直接访问它。如果使用对象剩余解构,则将禁用此优化。我们有一个lint 规则来防止这种陷阱。
您可以使用 select 选项来选择组件应订阅的数据子集。这对于高度优化的数据转换或避免不必要的重新渲染非常有用。
export const useTodos = (select) => {
return useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
select,
})
}
export const useTodoCount = () => {
return useTodos((data) => data.length)
}
export const useTodos = (select) => {
return useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
select,
})
}
export const useTodoCount = () => {
return useTodos((data) => data.length)
}
使用 useTodoCount 自定义钩子的组件仅在待办事项的长度更改时才会重新渲染。如果例如待办事项的名称更改,它不会重新渲染。
注意:select 对成功缓存的数据进行操作,不适合抛出错误。错误的真实来源是 queryFn,返回错误的 select 函数会导致 data 为 undefined 且 isSuccess 为 true。如果您希望查询在数据不正确时失败,我们建议在 queryFn 中处理错误,或者如果您有与缓存无关的错误情况,则在查询钩子之外处理错误。
select 函数仅在以下情况下才会重新运行:
这意味着如上所示的内联 select 函数将在每次渲染时运行。为避免这种情况,您可以将 select 函数包装在 useCallback 中,或者如果它没有任何依赖项,则将其提取到一个稳定的函数引用中:
// 包装在 useCallback 中
export const useTodoCount = () => {
return useTodos(useCallback((data) => data.length, []))
}
// 包装在 useCallback 中
export const useTodoCount = () => {
return useTodos(useCallback((data) => data.length, []))
}
// 提取到一个稳定的函数引用
const selectTodoCount = (data) => data.length
export const useTodoCount = () => {
return useTodos(selectTodoCount)
}
// 提取到一个稳定的函数引用
const selectTodoCount = (data) => data.length
export const useTodoCount = () => {
return useTodos(selectTodoCount)
}
有关这些主题的深入指南,请阅读社区资源中的 React Query 渲染优化。