译零 TransZero · TanStack Query 中文文档
主站

初始查询数据

更新于 2026-11-02 React 查看英文原文 ↗

在你需要使用查询之前,有多种方式可将初始数据提供给缓存:

使用 initialData 预填充查询

有时你已在应用中拥有某个查询的初始数据,可直接将其提供给查询。若确实如此,你可使用 config.initialData 选项来设置查询的初始数据,并跳过初始加载状态!

重要:initialData 会被持久化到缓存中,因此不建议向该选项提供占位数据、部分数据或不完整数据;而应改用 placeholderData。

TypeScript
1const result = useQuery({2  queryKey: ['todos'],3  queryFn: () => fetch('/todos'),4  initialData: initialTodos,5})

staleTime 和 initialDataUpdatedAt

默认情况下,initialData 被视为完全新鲜的数据,就像刚刚拉取的一样。这也意味着它会影响 staleTime 选项对其的解释方式。

  • 若你为查询观察者配置了 initialData,但未配置 staleTime(即采用默认 staleTime: 0),则该查询在挂载时会立即重新拉取:

TypeScript
1  // Will show initialTodos immediately, but also immediately refetch todos after mount2  const result = useQuery({3    queryKey: ['todos'],4    queryFn: () => fetch('/todos'),5    initialData: initialTodos,6  })
  • 若你为查询观察者同时配置了 initialData 和 staleTime(值为 1000 毫秒),则该数据将在相同时间内被视为新鲜数据,如同刚从你的查询函数中拉取一样。

TypeScript
1  // Show initialTodos immediately, but won't refetch until another interaction event is encountered after 1000 ms2  const result = useQuery({3    queryKey: ['todos'],4    queryFn: () => fetch('/todos'),5    initialData: initialTodos,6    staleTime: 1000,7  })
  • 那么,若你的 initialData 并非完全新鲜呢?这就引出了最后一种配置方式——它实际上最准确,且使用了一个名为 initialDataUpdatedAt 的选项。该选项允许你传入一个以毫秒为单位的 JavaScript 时间戳,表示初始数据本身最后一次更新的时间(例如 Date.now() 提供的时间戳)。请注意:若你拥有 Unix 时间戳,则需将其乘以 1000 来转换为 JavaScript 时间戳。

TypeScript
1  // Show initialTodos immediately, but won't refetch until another interaction event is encountered after 1000 ms2  const result = useQuery({3    queryKey: ['todos'],4    queryFn: () => fetch('/todos'),5    initialData: initialTodos,6    staleTime: 60 * 1000, // 1 minute7    // This could be 10 seconds ago or 10 minutes ago8    initialDataUpdatedAt: initialTodosUpdatedTimestamp, // eg. 16084124200529  })

此选项使 staleTime 能够发挥其原始作用——判断数据的新鲜程度;同时,若 initialData 的更新时间早于 staleTime 所定义的时限,该数据仍会在挂载时被重新拉取。在上述示例中,我们的数据需在一分钟内保持新鲜,而我们可通过提示查询初始数据的最后更新时间,让查询自行决定是否需要再次重新拉取。

若你更希望将数据视为预取数据,我们建议你提前使用 query API 填充缓存,从而让你能独立于 initialData 来配置 staleTime。

初始数据函数

若获取查询初始数据的过程开销较大,或你并不希望每次渲染都执行该过程,你可以将一个函数作为 initialData 的值传入。该函数仅在查询初始化时执行一次,从而节省宝贵的内存和/或 CPU 资源:

TypeScript
1const result = useQuery({2  queryKey: ['todos'],3  queryFn: () => fetch('/todos'),4  initialData: () => getExpensiveTodos(),5})

来自缓存的初始数据

在某些情况下,你可能能够从另一个查询的缓存结果中获取当前查询的初始数据。一个典型示例是:在待办事项列表查询(todos list query)的缓存数据中搜索某个单独的待办事项,再将该结果用作该单独待办事项查询的初始数据:

TypeScript
1const result = useQuery({2  queryKey: ['todo', todoId],3  queryFn: () => fetch('/todos'),4  initialData: () => {5    // Use a todo from the 'todos' query as the initial data for this todo query6    return queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId)7  },8})

使用 initialDataUpdatedAt 从缓存获取初始数据

从缓存中获取初始数据意味着你用于查找初始数据的源查询很可能已过期。此时,与其使用人为设定的 staleTime 来阻止查询立即重新拉取,不如将源查询的 dataUpdatedAt 传递给 initialDataUpdatedAt。这将为查询实例提供所有必要信息,使其能自主判断是否以及何时需要重新拉取,无论是否提供了初始数据。

TypeScript
1const result = useQuery({2  queryKey: ['todos', todoId],3  queryFn: () => fetch(`/todos/${todoId}`),4  initialData: () =>5    queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId),6  initialDataUpdatedAt: () =>7    queryClient.getQueryState(['todos'])?.dataUpdatedAt,8})

条件性地从缓存获取初始数据

若你用于查找初始数据的源查询已过期,你可能根本不想使用缓存数据,而是直接从服务端拉取。为简化这一决策过程,你可以改用 queryClient.getQueryState 方法,以获取有关源查询的更多信息,包括一个 state.dataUpdatedAt 时间戳——你可据此判断该查询对你的需求而言是否“足够新鲜”:

TypeScript
1const result = useQuery({2  queryKey: ['todo', todoId],3  queryFn: () => fetch(`/todos/${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 === todoId)12    }13 14    // Otherwise, return undefined and let it fetch from a hard loading state!15  },16})

进一步阅读

关于 Initial Data 与 Placeholder Data 的对比,请参阅 TkDodo 的文章。

本页为 TanStack Query 文档(React 版)的非官方中文翻译,原文由上游团队维护,遵循 MIT 许可。 译文如有偏差,以英文原文为准。

译自上游 59d11de