初始查询数据
在需要之前,有多种方式可将初始数据提供给查询的缓存:
- 声明式:
- 向查询提供
initialData,以便在缓存为空时预填充其缓存
- 向查询提供
- 命令式:
使用 initialData 预填充查询
有时你已在应用中拥有某个查询的初始数据,可直接将其提供给查询。若确实如此,你可使用 config.initialData 选项来设置查询的初始数据,并跳过初始加载状态!
重要:
initialData会被持久化到缓存中,因此不建议为此选项提供占位数据、部分数据或不完整数据;而应改用placeholderData
1todosQuery = injectQuery(() => ({2 queryKey: ['todos'],3 queryFn: () => fetch('/todos'),4 initialData: initialTodos,5}))staleTime 和 initialDataUpdatedAt
默认情况下,initialData 被视为完全新鲜的数据,如同刚刚拉取的一样。这也意味着它会影响 staleTime 选项对其的解释方式。
若你为查询观察者配置了
initialData,但未配置staleTime(即采用默认staleTime: 0),则查询会立即重新拉取:
1// Will show initialTodos immediately, but also immediately refetch todos2// when an instance of the component or service is created3todosQuery = injectQuery(() => ({4 queryKey: ['todos'],5 queryFn: () => fetch('/todos'),6 initialData: initialTodos,7}))若你为查询观察者配置了
initialData和staleTime值为1000ms,则该数据在此时间段内均被视为新鲜数据,如同刚从查询函数拉取的一样。
1// Show initialTodos immediately, but won't refetch until2// another interaction event is encountered after 1000 ms3todosQuery = injectQuery(() => ({4 queryKey: ['todos'],5 queryFn: () => fetch('/todos'),6 initialData: initialTodos,7 staleTime: 1000,8}))那么,若你的
initialData并非完全新鲜呢?这就引出了最后一种配置方式——实际上也是最准确的方式,它使用一个名为initialDataUpdatedAt的选项。该选项允许你传入一个以毫秒为单位的 JavaScript 时间戳,表示初始数据本身最后一次更新的时间,例如Date.now()所提供的值。请注意,若你拥有 Unix 时间戳,则需将其乘以1000转换为 JavaScript 时间戳。
1// Show initialTodos immediately, but won't refetch until2// another interaction event is encountered after 1000 ms3todosQuery = injectQuery(() => ({4 queryKey: ['todos'],5 queryFn: () => fetch('/todos'),6 initialData: initialTodos,7 staleTime: 60 * 1000, // 1 minute8 // This could be 10 seconds ago or 10 minutes ago9 initialDataUpdatedAt: initialTodosUpdatedTimestamp, // eg. 160841242005210})) 此选项使 staleTime 可用于其原始目的——确定数据需保持多新鲜;同时,若 initialData 的更新时间早于 staleTime 所定义的时限,查询仍可在初始化时重新拉取数据。在上述示例中,我们的数据需在一分钟内保持新鲜,且我们可向查询提示初始数据最后一次更新的时间,从而让查询自行判断是否需要再次重新拉取。
若你更倾向于将数据视为预取数据,我们建议你提前使用
queryAPI 填充缓存,从而让你能独立于initialData来配置staleTime。
初始数据函数
若获取查询初始数据的过程开销较大,或你不希望每次服务或组件实例化时都执行该过程,你可以将一个函数作为 initialData 的值传入。该函数仅在查询初始化时执行一次,从而节省宝贵的内存和/或 CPU 资源:
1todosQuery = injectQuery(() => ({2 queryKey: ['todos'],3 queryFn: () => fetch('/todos'),4 initialData: () => getExpensiveTodos(),5}))来自缓存的初始数据
在某些情况下,你可能能够从另一查询的缓存结果中获取某查询的初始数据。一个典型示例是:在待办事项列表查询的缓存数据中搜索某个单独的待办事项,再将该结果用作单独待办事项查询的初始数据:
1todoQuery = injectQuery(() => ({2 queryKey: ['todo', this.todoId()],3 queryFn: () => fetch('/todos'),4 initialData: () => {5 // Use a todo from the 'todos' query as the initial data for this todo query6 return this.queryClient7 .getQueryData(['todos'])8 ?.find((d) => d.id === this.todoId())9 },10}))使用 initialDataUpdatedAt 从缓存获取初始数据
从缓存获取初始数据意味着你所使用的源查询很可能已过期。与其使用人为设定的 staleTime 来阻止查询立即重新拉取,不如将源查询的 dataUpdatedAt 传递给 initialDataUpdatedAt。这可为查询实例提供全部必要信息,使其能自主判断是否以及何时需要重新拉取,无论是否提供了初始数据。
1todoQuery = injectQuery(() => ({2 queryKey: ['todos', this.todoId()],3 queryFn: () => fetch(`/todos/${this.todoId()}`),4 initialData: () =>5 queryClient.getQueryData(['todos'])?.find((d) => d.id === this.todoId()),6 initialDataUpdatedAt: () =>7 queryClient.getQueryState(['todos'])?.dataUpdatedAt,8}))条件性地从缓存获取初始数据
若你所使用的源查询已过期,你可能根本不想使用缓存数据,而是直接从服务器拉取。为简化此决策过程,你可以改用 queryClient.getQueryState 方法,以获取有关源查询的更多信息,包括一个 state.dataUpdatedAt 时间戳;你可据此判断该查询对你的需求而言是否“足够新鲜”:
1todoQuery = injectQuery(() => ({2 queryKey: ['todo', this.todoId()],3 queryFn: () => fetch(`/todos/${this.todoId()}`),4 initialData: () => {5 // Get the query state6 const state = queryClient.getQueryState(['todos'])7 8 // If the query exists and has data that is no older than 10 seconds...9 if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) {10 // return the individual todo11 return state.data.find((d) => d.id === this.todoId())12 }13 14 // Otherwise, return undefined and let it fetch from a hard loading state!15 },16}))