Skip to main content

Initial Query Data

还没翻译译自英文(tanstack.com)本页 19 段都还没译

模型写的

背景主张方法结果分析局限

There are many ways to supply initial data for a query to the cache before you need it:There are many ways to supply initial data for a query to the cache before you need it:查询缓存初始数据有多种提供方式 随后列出的清单给出了声明式与命令式两类在需要之前填充的方法查询缓存初始数据有多种提供方式初始数据

  • Declaratively:
    • Provide initialData to a query to prepopulate its cache if empty
      Provide initialData to a query to prepopulate its cache if empty
      提供 initialData 可在缓存为空时预填充查询 这一声明式选项仅在缓存尚无数据时填充
      提供 initialData 可在缓存为空时预填充查询
      initialData
  • Imperatively:
    • Prefetch the data using queryClient.query
      使用 queryClient.prefetchQuery 命令式预取数据 预取会提前填充缓存,使查询挂载时即带有数据
      使用 queryClient.prefetchQuery 命令式预取数据
      prefetchQuery
    • Manually place the data into the cache using queryClient.setQueryData
      用 queryClient.setQueryData 手动将数据放入缓存 该方法直接按查询键把值写入缓存
      用 queryClient.setQueryData 手动将数据放入缓存
      setQueryData

Using initialData to prepopulate a query

config.initialData 选项预填充查询并跳过初始加载

There may be times when you already have the initial data for a query available in your app and can simply provide it directly to your query. If and when this is the case, you can use the config.initialData option to set the initial data for a query and skip the initial loading state!There may be times when you already have the initial data for a query available in your app and can simply provide it directly to your query. If and when this is the case, you can use the config.initialData option to set the initial data for a query and skip the initial loading state!用 config.initialData 设置初始数据并跳过初始加载 当应用中已有数据时,可直接将其提供给查询用 config.initialData 设置初始数据并跳过初始加载config.initialData

IMPORTANT: initialData is persisted to the cache, so it is not recommended to provide placeholder, partial or incomplete data to this option and instead use placeholderData

IMPORTANT: initialData is persisted to the cache, so it is not recommended to provide placeholder, partial or incomplete data to this option and instead use placeholderData
initialData 会持久化,应避免提供占位或不完整数据 由于 initialData 会写入缓存,不完整值应改用 placeholderData
initialData 会持久化,应避免提供占位或不完整数据
initialData 持久化
const result = useQuery({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: initialTodos,
})

staleTime and initialDataUpdatedAt

initialData 被视为新鲜数据,并与 staleTime 和重新获取交互

By default, initialData is treated as totally fresh, as if it were just fetched. This also means that it will affect how it is interpreted by the staleTime option.By default, initialData is treated as totally fresh, as if it were just fetched. This also means that it will affect how it is interpreted by the staleTime option.initialData 被视为完全新鲜,就像刚获取一样 这一默认会影响 staleTime 如何解读初始数据initialData 被视为完全新鲜,就像刚获取一样initialData 新鲜度

  • If you configure your query observer with initialData, and no staleTime (the default staleTime: 0), the query will immediately refetch when it mounts:

    If you configure your query observer with initialData, and no staleTime (the default staleTime: 0), the query will immediately refetch when it mounts:
    默认 staleTime 为 0 时,带 initialData 的查询挂载即重新获取 数据立即被视为过期,因此查询挂载时会触发重新获取
    默认 staleTime 为 0 时,带 initialData 的查询挂载即重新获取
    staleTime 0
    // Will show initialTodos immediately, but also immediately refetch todos after mount
    const result = useQuery({
      queryKey: ['todos'],
      queryFn: () => fetch('/todos'),
      initialData: initialTodos,
    })
  • If you configure your query observer with initialData and a staleTime of 1000 ms, the data will be considered fresh for that same amount of time, as if it was just fetched from your query function.

    If you configure your query observer with initialData and a staleTime of 1000 ms, the data will be considered fresh for that same amount of time, as if it was just fetched from your query function.
    staleTime 为 1000 毫秒时 initialData 在那段时间内保持新鲜 在该时段内数据被视为就像刚从查询函数获取一样
    staleTime 为 1000 毫秒时 initialData 在那段时间内保持新鲜
    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,
    })
  • So what if your initialData isn't totally fresh? That leaves us with the last configuration that is actually the most accurate and uses an option called initialDataUpdatedAt. This option allows you to pass a numeric JS timestamp in milliseconds of when the initialData itself was last updated, e.g. what Date.now() provides. Take note that if you have a unix timestamp, you'll need to convert it to a JS timestamp by multiplying it by 1000.

    That leaves us with the last configuration that is actually the most accurate and uses an option called initialDataUpdatedAt. This option allows you to pass a numeric JS timestamp in milliseconds of when the initialData itself was last updated, e.g. Take note that if you have a unix timestamp, you'll need to convert it to a JS timestamp by multiplying it by 1000.
    initialDataUpdatedAt 接收数据最后更新时间的 JS 时间戳 Unix 时间戳需乘以 1000 才能转换为 JS 时间戳
    initialDataUpdatedAt 接收数据最后更新时间的 JS 时间戳
    initialDataUpdatedAt
    // 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
    })

    This option allows the staleTime to be used for its original purpose, determining how fresh the data needs to be, while also allowing the data to be refetched on mount if the initialData is older than the staleTime. In the example above, our data needs to be fresh within 1 minute, and we can hint to the query when the initialData was last updated so the query can decide for itself whether the data needs to be refetched again or not.

    If you would rather treat your data as prefetched data, we recommend that you use the query api to populate the cache beforehand, thus letting you configure your staleTime independently from your initialData.

Initial Data Function

作为 initialData 的函数只在查询初始化时执行一次,节省 CPU

If the process for accessing a query's initial data is intensive or just not something you want to perform on every render, you can pass a function as the initialData value. This function will be executed only once when the query is initialized, saving you precious memory and/or CPU:If the process for accessing a query's initial data is intensive or just not something you want to perform on every render, you can pass a function as the initialData value. This function will be executed only once when the query is initialized, saving you precious memory and/or CPU:把函数作为 initialData,仅在初始化时执行一次 当获取初始数据开销大或不宜每次渲染执行时,这可节省内存和 CPU把函数作为 initialData,仅在初始化时执行一次initialData 函数

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

Initial Data from Cache

初始数据可以来自另一个查询的缓存结果

In some circumstances, you may be able to provide the initial data for a query from the cached result of another. A good example of this would be searching the cached data from a todos list query for an individual todo item, then using that as the initial data for your individual todo query:In some circumstances, you may be able to provide the initial data for a query from the cached result of another. A good example of this would be searching the cached data from a todos list query for an individual todo item, then using that as the initial data for your individual todo query:初始数据可来自另一个查询的缓存结果 例如,从 todos 列表缓存中查找单个待办项初始数据可来自另一个查询的缓存结果缓存结果

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)
  },
})

Initial Data from the cache with initialDataUpdatedAt

将源查询的 dataUpdatedAt 传给 initialDataUpdatedAt,而非 staleTime

Getting initial data from the cache means the source query you're using to look up the initial data from is likely old. Instead of using an artificial staleTime to keep your query from refetching immediately, it's suggested that you pass the source query's dataUpdatedAt to initialDataUpdatedAt. This provides the query instance with all the information it needs to determine if and when the query needs to be refetched, regardless of initial data being provided.Getting initial data from the cache means the source query you're using to look up the initial data from is likely old. Instead of using an artificial staleTime to keep your query from refetching immediately, it's suggested that you pass the source query's dataUpdatedAt to initialDataUpdatedAt. This provides the query instance with all the information it needs to determine if and when the query needs to be refetched, regardless of initial data being provided.将源查询的 dataUpdatedAt 传给 initialDataUpdatedAt 这让查询自行决定是否及何时重新获取,而非伪造 staleTime将源查询的 dataUpdatedAt 传给 initialDataUpdatedAtdataUpdatedAt

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

Conditional Initial Data from Cache

getQueryState 让你仅在缓存数据足够新鲜时才条件性地使用它

If the source query you're using to look up the initial data from is old, you may not want to use the cached data at all and just fetch from the server. To make this decision easier, you can use the queryClient.getQueryState method instead to get more information about the source query, including a state.dataUpdatedAt timestamp you can use to decide if the query is "fresh" enough for your needs:If the source query you're using to look up the initial data from is old, you may not want to use the cached data at all and just fetch from the server. To make this decision easier, you can use the queryClient.getQueryState method instead to get more information about the source query, including a state.dataUpdatedAt timestamp you can use to decide if the query is "fresh" enough for your needs:用 queryClient.getQueryState 条件性地使用缓存数据 其 state.dataUpdatedAt 时间戳可判断源查询是否足够新鲜用 queryClient.getQueryState 条件性地使用缓存数据getQueryState

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!
  },
})

Further reading

TkDodo 的文章对比了 Initial Data 与 Placeholder Data

For a comparison between Initial Data and Placeholder Data, see the article by TkDodo.For a comparison between Initial Data and Placeholder Data, see the article by TkDodo.TkDodo 的文章对比了 Initial Data 与 Placeholder Data 该文进一步阐述了这两个选项之间的区别TkDodo 的文章对比了 Initial Data 与 Placeholder DataInitial Data