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