框架
版本

占位符查询数据

什么是占位符数据?

占位符数据允许查询表现得好像它已经有数据一样,类似于 initialData 选项,但数据不会持久化到缓存中。这对于您有足够的局部(或伪造)数据以成功渲染查询,同时实际数据在后台获取的情况非常有用。

示例:单个博客文章查询可以从父博客文章列表中提取“预览”数据,该列表仅包含标题和文章正文的一小段。您不希望将此部分数据持久化到单个查询的查询结果中,但在实际查询完成获取整个对象时,它对于尽快显示内容布局非常有用。

有几种方法��以在需要之前为查询的缓存提供占位符数据:

当我们使用 placeholderData 时,我们的查询将不会处于 pending 状态——它将以 success 状态开始,因为我们有要显示的 data——即使该数据只是“占位符”数据。为了将其与“真实”数据区分开来,我们还将在查询结果上将 isPlaceholderData 标志设置为 true

作为值的占位符数据

ts
class TodosComponent {
  result = injectQuery(() => ({
    queryKey: ['todos'],
    queryFn: () => fetch('/todos'),
    placeholderData: placeholderTodos,
  }))
}
class TodosComponent {
  result = injectQuery(() => ({
    queryKey: ['todos'],
    queryFn: () => fetch('/todos'),
    placeholderData: placeholderTodos,
  }))
}

initialData 类似,如果您传递给 placeholderData 的值是引用不稳定的,那么您应该对其进行记忆化或将其声明在组件范围之外。

作为函数的占位符数据

placeholderData 也可以是一个函数,您可以在其中访问“先前”成功查询的数据和查询元信息。这对于您希望将一个查询的数据用作另一个查询的占位符数据的情况非常有用。当 QueryKey 更改时,例如从 ['todos', 1] 更改为 ['todos', 2],我们可以继续显示“旧”数据,而不必在数据从一个查询_转换_到下一个查询时显示加载微调器。有关更多信息,请参阅分页查询

ts
class TodosComponent {
  result = injectQuery(() => ({
    queryKey: ['todos', id()],
    queryFn: () => fetch(`/todos/${id}`),
    placeholderData: (previousData, previousQuery) => previousData,
  }))
}
class TodosComponent {
  result = injectQuery(() => ({
    queryKey: ['todos', id()],
    queryFn: () => fetch(`/todos/${id}`),
    placeholderData: (previousData, previousQuery) => previousData,
  }))
}

来自缓存的占位符数据

在某些情况下,您可能能够从另一个查询的缓存结果中为您的查询提供占位符数据。一个很好的例子是从博客文章列表查询中搜索文章的预览版本,然后将其用作单个文章查询的占位符数据:

ts
export class BlogPostComponent {
  // 在 Angular 支持基于信号的输入之前,我们必须设置一个信号
  @Input({ required: true, alias: 'postId' })
  set _postId(value: number) {
    this.postId.set(value)
  }
  postId = signal(0)
  queryClient = inject(QueryClient)

  result = injectQuery(() => ({
    queryKey: ['blogPost', this.postId()],
    queryFn: () => fetch(`/blogPosts/${this.postId()}`),
    placeholderData: () => {
      // 使用来自“blogPosts”查询的较小/预览版本的 blogPost
      // 作为此 blogPost 查询的占位符数据
      return queryClient
        .getQueryData(['blogPosts'])
        ?.find((d) => d.id === this.postId())
    },
  }))
}
export class BlogPostComponent {
  // 在 Angular 支持基于信号的输入之前,我们必须设置一个信号
  @Input({ required: true, alias: 'postId' })
  set _postId(value: number) {
    this.postId.set(value)
  }
  postId = signal(0)
  queryClient = inject(QueryClient)

  result = injectQuery(() => ({
    queryKey: ['blogPost', this.postId()],
    queryFn: () => fetch(`/blogPosts/${this.postId()}`),
    placeholderData: () => {
      // 使用来自“blogPosts”查询的较小/预览版本的 blogPost
      // 作为此 blogPost 查询的占位符数据
      return queryClient
        .getQueryData(['blogPosts'])
        ?.find((d) => d.id === this.postId())
    },
  }))
}

推荐阅读