初始查询数据

在需要之前,有很多方法可以为查询向缓存提供初始数据

使用 initialData 预填充查询

有时,您可能已经在您的应用程序中拥有查询的初始数据,并且可以直接将其提供给您的查询。 如果是这种情况,您可以使用 config.initialData 选项为查询设置初始数据并跳过初始加载状态!

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

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

staleTimeinitialDataUpdatedAt

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

  • 如果您使用 initialData 配置您的查询观察者,并且没有 staleTime(默认 staleTime: 0),则查询将立即重新获取
ts
// Will show initialTodos immediately, but also immediately refetch todos
// when an instance of the component or service is created
result = injectQuery(() => ({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: initialTodos,
}))
// Will show initialTodos immediately, but also immediately refetch todos
// when an instance of the component or service is created
result = injectQuery(() => ({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: initialTodos,
}))
  • 如果您使用 initialDatastaleTime1000 毫秒配置您的查询观察者,则数据将被视为在该相同的时间段内是新鲜的,就像刚从您的查询函数中获取的一样。
ts
// Show initialTodos immediately, but won't refetch until
// another interaction event is encountered after 1000 ms
result = injectQuery(() => ({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: initialTodos,
  staleTime: 1000,
}))
// Show initialTodos immediately, but won't refetch until
// another interaction event is encountered after 1000 ms
result = injectQuery(() => ({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: initialTodos,
  staleTime: 1000,
}))
  • 那么,如果您的 initialData 不是完全新鲜的呢? 这就引出了最后一个配置,它实际上是最准确的,并使用一个名为 initialDataUpdatedAt 的选项。 此选项允许您传递一个数字 JS 时间戳(以毫秒为单位),表示初始数据本身上次更新的时间,例如 Date.now() 提供的。 请注意,如果您有 Unix 时间戳,则需要将其乘以 1000 才能转换为 JS 时间戳。
ts
// Show initialTodos immediately, but won't refetch until
// another interaction event is encountered after 1000 ms
result = injectQuery(() => ({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: initialTodos,
  staleTime: 60 * 1000, // 1 minute
  // This could be 10 seconds ago or 10 minutes ago
  initialDataUpdatedAt: initialTodosUpdatedTimestamp, // eg. 1608412420052
}))
// Show initialTodos immediately, but won't refetch until
// another interaction event is encountered after 1000 ms
result = injectQuery(() => ({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: initialTodos,
  staleTime: 60 * 1000, // 1 minute
  // This could be 10 seconds ago or 10 minutes ago
  initialDataUpdatedAt: initialTodosUpdatedTimestamp, // eg. 1608412420052
}))

此选项允许将 staleTime 用于其原始目的,即确定数据需要有多新鲜,同时还允许在初始化时重新获取数据(如果 initialDatastaleTime 旧)。 在上面的示例中,我们的数据需要在 1 分钟内保持新鲜,我们可以向查询提示 initialData 上次更新的时间,以便查询可以自行决定是否需要再次重新获取数据。

如果您宁愿将您的数据视为预取数据,我们建议您使用 prefetchQueryfetchQuery API 提前填充缓存,从而让您可以独立于您的 initialData 配置您的 staleTime

初始数据函数

如果访问查询的初始数据的过程很密集,或者只是您不想在每个服务或组件实例上执行的操作,您可以将一个函数作为 initialData 值传递。 此函数仅在查询初始化时执行一次,从而节省您宝贵的内存和/或 CPU

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

来自缓存的初始数据

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

ts
result = injectQuery(() => ({
  queryKey: ['todo', this.todoId()],
  queryFn: () => fetch('/todos'),
  initialData: () => {
    // Use a todo from the 'todos' query as the initial data for this todo query
    return this.queryClient
      .getQueryData(['todos'])
      ?.find((d) => d.id === this.todoId())
  },
}))
result = injectQuery(() => ({
  queryKey: ['todo', this.todoId()],
  queryFn: () => fetch('/todos'),
  initialData: () => {
    // Use a todo from the 'todos' query as the initial data for this todo query
    return this.queryClient
      .getQueryData(['todos'])
      ?.find((d) => d.id === this.todoId())
  },
}))

来自缓存的初始数据,带有 initialDataUpdatedAt

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

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

来自缓存的条件初始数据

如果您用于查找初始数据的源查询很旧,您可能根本不想使用缓存的数据,而只想从服务器获取数据。 为了使此决定更容易,您可以改用 queryClient.getQueryState 方法来获取有关源查询的更多信息,包括一个 state.dataUpdatedAt 时间戳,您可以使用该时间戳来决定查询是否“足够新鲜”以满足您的需求

ts
result = injectQuery(() => ({
  queryKey: ['todo', this.todoId()],
  queryFn: () => fetch(`/todos/${this.todoId()}`),
  initialData: () => {
    // Get the query state
    const state = queryClient.getQueryState(['todos'])

    // If the query exists and has data that is no older than 10 seconds...
    if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) {
      // return the individual todo
      return state.data.find((d) => d.id === this.todoId())
    }

    // Otherwise, return undefined and let it fetch from a hard loading state!
  },
}))
result = injectQuery(() => ({
  queryKey: ['todo', this.todoId()],
  queryFn: () => fetch(`/todos/${this.todoId()}`),
  initialData: () => {
    // Get the query state
    const state = queryClient.getQueryState(['todos'])

    // If the query exists and has data that is no older than 10 seconds...
    if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) {
      // return the individual todo
      return state.data.find((d) => d.id === this.todoId())
    }

    // Otherwise, return undefined and let it fetch from a hard loading state!
  },
}))