预取
预取的目标是在用户导航到页面或尝试加载一些已知内容 *之前* 获取数据。
有几种情况你可能想要这样做,但一些非常常见的用例是
- 用户将鼠标悬停在导航元素上
- 用户将鼠标悬停在作为链接的列表元素上
- 用户将鼠标悬停在下一个分页按钮上
- 用户导航到页面,并且你知道树中的一些组件将需要这些数据。这样,你可以防止获取瀑布。
使用 React Hooks 预取
类似于 useMutation
钩子,usePrefetch
钩子不会自动运行 - 它返回一个“触发函数”,可用于启动行为。
它接受两个参数:第一个是您在 API 服务中定义的查询操作的键 定义在您的 API 服务中,第二个是包含两个可选参数的对象。
usePrefetch 签名
export type PrefetchOptions =
| { force?: boolean }
| {
ifOlderThan?: false | number;
};
usePrefetch<EndpointName extends QueryKeys<Definitions>>(
endpointName: EndpointName,
options?: PrefetchOptions
): (arg: QueryArgFrom<Definitions[EndpointName]>, options?: PrefetchOptions) => void;
自定义钩子行为
您可以在声明钩子或在调用点指定这些预取选项。调用点将优先于默认值。
ifOlderThan
- (默认:false
|number
) - 数字以秒为单位- 如果指定,它将仅在
new Date()
和上次fulfilledTimeStamp
之间的差值大于给定值时运行查询。
- 如果指定,它将仅在
force
- 如果
force: true
,它将忽略ifOlderThan
值(如果设置),即使查询存在于缓存中,也会运行查询。
- 如果
触发函数行为
- 触发函数始终返回
void
。 - 如果在声明或在调用点设置了
force: true
,则无论如何都会运行查询。唯一例外是如果相同的查询已经在进行中。 - 如果没有指定选项并且查询存在于缓存中,则不会执行查询。
- 如果没有指定选项并且查询不存在于缓存中,则将执行查询。
- 假设您在树中有一个
useQuery
钩子,它订阅了您正在预取的相同查询。useQuery
将返回{isLoading: true, isFetching: true, ...rest
}
- 假设您在树中有一个
- 如果指定了
ifOlderThan
但评估结果为 false 并且查询在缓存中,则不会执行查询。 - 如果指定了
ifOlderThan
并且评估结果为 true,即使存在现有缓存条目,也会执行查询。- 假设您在树中有一个
useQuery
钩子,它订阅了您正在预取的相同查询。useQuery
将返回{isLoading: false, isFetching: true, ...rest
}
- 假设您在树中有一个
usePrefetch 示例
function User() {
const prefetchUser = usePrefetch('getUser')
// Low priority hover will not fire unless the last request happened more than 35s ago
// High priority hover will _always_ fire
return (
<div>
<button onMouseEnter={() => prefetchUser(4, { ifOlderThan: 35 })}>
Low priority
</button>
<button onMouseEnter={() => prefetchUser(4, { force: true })}>
High priority
</button>
</div>
)
}
食谱:立即预取
在某些情况下,您可能希望立即预取资源。您可以在几行代码中实现这一点。
hooks/usePrefetchImmediately.ts
type EndpointNames = keyof typeof api.endpoints
export function usePrefetchImmediately<T extends EndpointNames>(
endpoint: T,
arg: Parameters<(typeof api.endpoints)[T]['initiate']>[0],
options: PrefetchOptions = {},
) {
const dispatch = useAppDispatch()
useEffect(() => {
dispatch(api.util.prefetch(endpoint, arg as any, options))
}, [])
}
// In a component
usePrefetchImmediately('getUser', 5)
无需 Hook 的预取
如果您没有使用 usePrefetch
hook,您可以在任何框架中自行重现相同的行为。
当您像下面这样调度 prefetch
thunk 时,您将看到与此处描述的完全相同的行为。
非 Hook 预取示例
store.dispatch(
api.util.prefetch(endpointName, arg, { force: false, ifOlderThan: 10 }),
)
您也可以调度查询操作,但您需要负责实现任何额外的逻辑。
手动预取的另一种方法
dispatch(api.endpoints[endpointName].initiate(arg, { forceRefetch: true }))
预取示例
基本预取
这是一个非常基本的示例,展示了如何在用户将鼠标悬停在下一个箭头时进行预取。这可能不是最佳解决方案,因为如果他们将鼠标悬停、点击,然后在不移动鼠标的情况下更改页面,我们不会知道要预取下一页,因为我们不会看到下一个 onMouseEnter
事件。在这种情况下,您需要自行处理。您也可以考虑自动预取下一页...
自动预取
从我们上一个示例开始,我们自动 prefetch
下一页,给人一种没有网络延迟的感觉。
预取所有已知页面
在 useQuery
初始化的第一个查询运行后,我们自动获取所有剩余页面。