框架
版本

初始查询数据

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

使用 initialData 预填充查询

有时候,您的应用中可能已经有了查询的初始数据,可以直接提供给查询。如果遇到这种情况,您可以使用 config.initialData 选项来设置查询的初始数据,并跳过初始加载状态!

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

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

staleTimeinitialDataUpdatedAt

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

  • 如果您使用 initialData 配置您的查询观察者,并且没有设置 staleTime(默认为 staleTime: 0),查询将在挂载时立即重新获取数据。

    tsx
    // Will show initialTodos immediately, but also immediately refetch todos after mount
    const result = useQuery(() => {
      queryKey: ['todos'],
      queryFn: () => fetch('/todos'),
      initialData: initialTodos,
    })
    
    // Will show initialTodos immediately, but also immediately refetch todos after mount
    const result = useQuery(() => {
      queryKey: ['todos'],
      queryFn: () => fetch('/todos'),
      initialData: initialTodos,
    })
    
  • 如果您使用 initialDatastaleTime1000 毫秒来配置您的查询观察者,数据将被视为在相同时间内是新鲜的,就像刚刚从您的查询函数中获取一样。

    tsx
    // Show initialTodos immediately, but won't refetch until another interaction event is encountered after 1000 ms
    const result = useQuery(() => {
      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
    const result = useQuery(() => {
      queryKey: ['todos'],
      queryFn: () => fetch('/todos'),
      initialData: initialTodos,
      staleTime: 1000,
    })
    
  • 那么,如果您的 initialData 不是全新的呢?这就引出了最后一个配置,它实际上是最准确的,并且使用了一个名为 initialDataUpdatedAt 的选项。此选项允许您传递一个数值型的 JS 时间戳(以毫秒为单位),表示 initialData 本身最后更新的时间,例如 Date.now() 提供的值。请注意,如果您有一个 Unix 时间戳,您需要将其乘以 1000 才能将其转换为 JS 时间戳。

    tsx
    // Show initialTodos immediately, but won't refetch until another interaction event is encountered after 1000 ms
    const result = useQuery(() => {
      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
    const result = useQuery(() => {
      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。

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

来自缓存的初始数据

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

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

来自缓存的初始数据与 initialDataUpdatedAt

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

tsx
const result = useQuery(() => {
  queryKey: ['todos', todoId],
  queryFn: () => fetch(`/todos/${todoId}`),
  initialData: () =>
    queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId),
  initialDataUpdatedAt: () =>
    queryClient.getQueryState(['todos'])?.dataUpdatedAt,
})
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 时间戳,您可以使用它来决定查询是否“新鲜”到满足您的需求。

tsx
const result = useQuery(() => {
  queryKey: ['todo', todoId],
  queryFn: () => fetch(`/todos/${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 === todoId)
    }

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

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

延伸阅读

有关 初始数据占位符数据 之间的比较,请查看 社区资源