Framework
Version

Paginierte / Verzögerte Abfragen

Das Rendern von paginierten Daten ist ein sehr gängiges UI-Muster und in TanStack Query "funktioniert es einfach", indem die Seiteninformationen in den Abfrageschlüssel aufgenommen werden.

tsx
const result = useQuery({
  queryKey: ['projects', page],
  queryFn: fetchProjects,
})
const result = useQuery({
  queryKey: ['projects', page],
  queryFn: fetchProjects,
})

Wenn Sie jedoch dieses einfache Beispiel ausführen, bemerken Sie möglicherweise etwas Seltsames.

Die Benutzeroberfläche springt zwischen den Zuständen success und pending hin und her, da jede neue Seite als völlig neue Abfrage behandelt wird.

Diese Erfahrung ist nicht optimal und leider ist es so, wie viele Tools heute bestehen. Aber nicht TanStack Query! Wie Sie sich vielleicht gedacht haben, verfügt TanStack Query über ein fantastisches Feature namens placeholderData, mit dem wir dies umgehen können.

Bessere paginierte Abfragen mit placeholderData

Betrachten Sie das folgende Beispiel, bei dem wir idealerweise einen pageIndex (oder Cursor) für eine Abfrage erhöhen möchten. Wenn wir useQuery verwenden würden, würde es technisch einwandfrei funktionieren, aber die Benutzeroberfläche würde zwischen den success und pending Zuständen springen, da für jede Seite oder jeden Cursor unterschiedliche Abfragen erstellt und zerstört werden. Durch das Setzen von placeholderData auf (previousData) => previousData oder die von TanStack Query exportierte Funktion keepPreviousData erhalten wir einige neue Dinge

  • Die Daten aus dem letzten erfolgreichen Abruf sind verfügbar, während neue Daten angefordert werden, auch wenn sich der Abfrageschlüssel geändert hat..
  • Wenn die neuen Daten eintreffen, werden die vorherigen data nahtlos ausgetauscht, um die neuen Daten anzuzeigen.
  • isPlaceholderData wird verfügbar gemacht, um zu wissen, welche Daten die Abfrage Ihnen gerade liefert.
vue
<script setup lang="ts">
import { ref, Ref } from 'vue'
import { useQuery, keepPreviousData } from '@tanstack/vue-query'

const fetcher = (page: Ref<number>) =>
  fetch(
    `https://jsonplaceholder.typicode.com/posts?_page=${page.value}&_limit=10`,
  ).then((response) => response.json())

const page = ref(1)
const { isPending, isError, data, error, isFetching, isPlaceholderData } =
  useQuery({
    queryKey: ['projects', page],
    queryFn: () => fetcher(page),
    placeholderData: keepPreviousData,
  })
const prevPage = () => {
  page.value = Math.max(page.value - 1, 1)
}
const nextPage = () => {
  if (!isPlaceholderData.value) {
    page.value = page.value + 1
  }
}
</script>

<template>
  <h1>Posts</h1>
  <p>Current Page: {{ page }} | Previous data: {{ isPlaceholderData }}</p>
  <button @click="prevPage">Prev Page</button>
  <button @click="nextPage">Next Page</button>
  <div v-if="isPending">Loading...</div>
  <div v-else-if="isError">An error has occurred: {{ error }}</div>
  <div v-else-if="data">
    <ul>
      <li v-for="item in data" :key="item.id">
        {{ item.title }}
      </li>
    </ul>
  </div>
</template>
<script setup lang="ts">
import { ref, Ref } from 'vue'
import { useQuery, keepPreviousData } from '@tanstack/vue-query'

const fetcher = (page: Ref<number>) =>
  fetch(
    `https://jsonplaceholder.typicode.com/posts?_page=${page.value}&_limit=10`,
  ).then((response) => response.json())

const page = ref(1)
const { isPending, isError, data, error, isFetching, isPlaceholderData } =
  useQuery({
    queryKey: ['projects', page],
    queryFn: () => fetcher(page),
    placeholderData: keepPreviousData,
  })
const prevPage = () => {
  page.value = Math.max(page.value - 1, 1)
}
const nextPage = () => {
  if (!isPlaceholderData.value) {
    page.value = page.value + 1
  }
}
</script>

<template>
  <h1>Posts</h1>
  <p>Current Page: {{ page }} | Previous data: {{ isPlaceholderData }}</p>
  <button @click="prevPage">Prev Page</button>
  <button @click="nextPage">Next Page</button>
  <div v-if="isPending">Loading...</div>
  <div v-else-if="isError">An error has occurred: {{ error }}</div>
  <div v-else-if="data">
    <ul>
      <li v-for="item in data" :key="item.id">
        {{ item.title }}
      </li>
    </ul>
  </div>
</template>

Verzögerte Infinite Query-Ergebnisse mit placeholderData

Obwohl nicht so häufig, funktioniert die Option placeholderData auch einwandfrei mit dem Hook useInfiniteQuery, sodass Sie Ihren Benutzern nahtlos weiterhin gecachte Daten anzeigen lassen können, während sich die Infinite Query-Schlüssel im Laufe der Zeit ändern.