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.
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.
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
<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>
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.