初始化查询数据

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

  • 声明式

    • 提供 initialData 给查询,以在缓存为空时预填充

  • 命令式

    • 使用 queryClient.prefetchQuery 预取数据

    • 使用 queryClient.setQueryData 手动将数据放入缓存

使用 initialData 预填充查询

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

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

const result = useQuery({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: initialTodos,
})

staleTimeinitialDataUpdatedAt

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

  • 如果你为查询观察器配置了 initialData 且没有配置 staleTime(默认 staleTime: 0),查询在挂载时会立即重新获取:

    // 立即显示 initialTodos,但在挂载后也会立即重新获取 todos
    const result = useQuery({
      queryKey: ['todos'],
      queryFn: () => fetch('/todos'),
      initialData: initialTodos,
    })
  • 如果你为查询观察器配置了 initialDatastaleTime1000 毫秒,数据将被视为在相同时间内都是新鲜的,就像刚刚从查询函数获取的一样。

    // 立即显示 initialTodos,但在 1000 毫秒后遇到另一个交互事件之前不会重新获取
    const result = useQuery({
      queryKey: ['todos'],
      queryFn: () => fetch('/todos'),
      initialData: initialTodos,
      staleTime: 1000,
    })
  • 那么,如果 initialData 并不完全新鲜呢?这引出了最后一种配置,实际上也是最准确的,它使用了一个名为 initialDataUpdatedAt 的选项。此选项允许你传递一个以毫秒为单位的 JS 时间戳,表示 initialData 本身最后一次更新的时间,例如 Date.now() 提供的时间。请注意,如果你有 Unix 时间戳,则需要通过乘以 1000 将其转换为 JS 时间戳。

    // 立即显示 initialTodos,但在 1000 毫秒后遇到另一个交互事件之前不会重新获取
    const result = useQuery({
      queryKey: ['todos'],
      queryFn: () => fetch('/todos'),
      initialData: initialTodos,
      staleTime: 60 * 1000, // 1 分钟
      // 这可能是 10 秒前或 10 分钟前
      initialDataUpdatedAt: initialTodosUpdatedTimestamp, // 例如 1608412420052
    })

    此选项允许 staleTime 用于其原始目的,即确定数据需要多新鲜,同时允许在 initialDatastaleTime 老时在挂载时重新获取数据。在上面的例子中,我们的数据需要在 1 分钟内保持新鲜,我们可以向查询提示 initialData 最后一次更新的时间,以便查询可以自行决定是否需要再次重新获取。

    如果你更愿意将数据视为预取数据,我们建议你使用 prefetchQueryfetchQuery API 来预先填充缓存,从而让你能够独立于 initialData 配置 staleTime

初始数据函数

如果访问查询初始数据的过程很耗资源,或者你不想在每次渲染时都执行,可以将一个函数作为 initialData 的值传递。该函数仅在查询初始化时执行一次,为你节省宝贵的内存和/或 CPU:

const result = useQuery({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: () => getExpensiveTodos(),
})

来自缓存的初始数据

在某些情况下,你可能能够从另一个查询的缓存结果中提供查询的初始数据。一个很好的例子是,从待办事项列表查询的缓存数据中搜索单个待办事项,然后将其用作单个待办事项查询的初始数据:

const result = useQuery({
  queryKey: ['todo', todoId],
  queryFn: () => fetch('/todos'),
  initialData: () => {
    // 使用 'todos' 查询中的待办事项作为此待办事项查询的初始数据
    return queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId)
  },
})

来自缓存的初始数据配合 initialDataUpdatedAt

从缓存中获取初始数据意味着你用来查找初始数据的源查询可能已经过时。与其使用人为的 staleTime 来阻止查询立即重新获取,建议你将源查询的 dataUpdatedAt 传递给 initialDataUpdatedAt。这为查询实例提供了确定是否以及何时需要重新获取查询所需的所有信息,无论是否提供了初始数据。

const result = useQuery({
  queryKey: ['todos', todoId],
  queryFn: () => fetch(`/todos/${todoId}`),
  initialData: () =>
    queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId),
  initialDataUpdatedAt: () =>
    queryClient.getQueryState(['todos'])?.dataUpdatedAt,
})

来自缓存的条件初始数据

如果你用来查找初始数据的源查询已经过时,你可能根本不想使用缓存数据,而是直接从服务器获取。为了更容易做出这个决定,你可以改用 queryClient.getQueryState 方法来获取有关源查询的更多信息,包括一个 state.dataUpdatedAt 时间戳,你可以用它来判断查询是否“足够新鲜”以满足你的需求:

const result = useQuery({
  queryKey: ['todo', todoId],
  queryFn: () => fetch(`/todos/${todoId}`),
  initialData: () => {
    // 获取查询状态
    const state = queryClient.getQueryState(['todos'])

    // 如果查询存在且数据不早于 10 秒...
    if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) {
      // 返回单个待办事项
      return state.data.find((d) => d.id === todoId)
    }

    // 否则,返回 undefined 并让它从硬加载状态开始获取!
  },
})

进一步阅读

要比较 初始数据占位符数据,请查看 社区资源。

最后更新于

这有帮助吗?