有几种方法可以在需要之前为查询的缓存提供初始数据:
有时,您可能已经在应用程序中拥有查询的初始数据,并且可以直接将其提供给您的查询。如果发生这种情况,您可以使用 config.initialData 选项来设置查询的初始数据,并跳过初始加载状态!
重要提示:initialData 会持久化到缓存中,因此不建议为此选项提供占位符、部分或不完整的数据,而应考虑使用 placeholderData
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
})
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
})
默认情况下,initialData 被视为完全新鲜的,就好像它刚刚被获取一样。这也意味着它会影响 staleTime 选项的解释方式。
如果您使用 initialData 配置查询观察者,并且没有 staleTime(默认 staleTime: 0),则查询将在挂载时立即重新获取:
// 将立即显示 initialTodos,但也会在挂载后立即重新获取 todos
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
})
// 将立即显示 initialTodos,但也会在挂载后立即重新获取 todos
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
})
如果您使用 initialData 和 staleTime(例如 1000 毫秒)配置查询观察者,则数据将在相同的时间内被视为新鲜的,就好像它刚刚从您的查询函数中获取一样。
// 立即显示 initialTodos,但在 1000 毫秒后遇到另一个交互事件之前不会重新获取
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
staleTime: 1000,
})
// 立即显示 initialTodos,但在 1000 毫秒后遇到另一个交互事件之前不会重新获取
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
staleTime: 1000,
})
那么,如果您的 initialData 并不完全新鲜怎么办?这就引出了最后一个配置,它实际上是最准确的,并使用一个名为 initialDataUpdatedAt 的选项。此选项允许您传递一个数字 JS 时间戳(以毫秒为单位,如 Date.now()),表示 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
})
// 立即显示 initialTodos,但在 1000 毫秒后遇到另一个交互事件之前不会重新获取
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: initialTodos,
staleTime: 60 * 1000, // 1 分钟
// 这可能是 10 秒前或 10 分钟前
initialDataUpdatedAt: initialTodosUpdatedTimestamp, // 例如 1608412420052
})
此选项允许 staleTime 用于其原始目的,确定数据需要多新鲜,同时还允许在 initialData 比 staleTime 旧时在挂载时重新获取数据。在上面的示例中,我们的数据需要在 1 分钟内保持新鲜,并且我们可以向查询提示 initialData 的上次更新时间,以便查询可以自行决定是否需要再次重新获取数据。
如果您希望将数据视为预取数据,我们建议您使用 prefetchQuery 或 fetchQuery API 来预先填充缓存,从而让您可以独立于 initialData 配置 staleTime
如果访问查询初始数据的过程很密集,或者只是您不想在每次渲染时都执行的操作,则可以将函数作为 initialData 值传递。此函数仅在初始化查询时执行一次,从而节省您宝贵的内存和/或 CPU:
const result = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
initialData: () => getExpensiveTodos(),
})
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)
},
})
const result = useQuery({
queryKey: ['todo', todoId],
queryFn: () => fetch('/todos'),
initialData: () => {
// 使用来自“todos”查询的待办事项作为此待办事项查询的初始数据
return queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId)
},
})
从缓存中获取初始数据意味着您用于查找初始数据的源查询可能很旧。与其使用人为的 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,
})
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 并让它从硬加载状态获取!
},
})
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 并让它从硬加载状态获取!
},
})