queryClient.getQueryData akzeptiert jetzt nur die Query-Schlüssel als ArgumentqueryClient.getQueryState akzeptiert jetzt nur die Query-Schlüssel als ArgumentrefetchInterval erhält nur query übergebenremove wurde aus useQuery entferntisDataEqual wurde aus useQuery entferntcacheTime umbenannt in gcTimeuseErrorBoundary wurde in throwOnError umbenanntError ist jetzt der Standardtyp für Fehler anstelle von unknownprefer-query-object-syntax wurde entferntkeepPreviousData entfernt zugunsten der Identitätsfunktion placeholderDatafocus-Ereignisnavigator.onLinecontext-Prop entfernt zugunsten einer benutzerdefinierten queryClient-InstanzrefetchPage entfernt zugunsten von maxPagesdehydrate APIinitialPageParamnull von getNextPageParam oder getPreviousPageParam zeigt jetzt an, dass keine weitere Seite verfügbar iststatus: loading wurde zu status: pending geändert und isLoading wurde zu isPending geändert und isInitialLoading wurde jetzt in isLoading umbenannthashQueryKey wurde in hashKey umbenanntuseQueries-Composable gibt jetzt ref anstelle von reactive zurückcombine-Option für useQueriesfine grained storage persistervue-query-Composables in injectionContext auszuführenv5 ist eine Hauptversion, daher gibt es einige Breaking Changes zu beachten
useQuery und ähnliche Funktionen hatten früher viele Überladungen in TypeScript: verschiedene Möglichkeiten, wie die Funktion aufgerufen werden konnte. Dies war nicht nur schwierig zu warten, sondern erforderte auch eine Laufzeitüberprüfung, um festzustellen, welche Typen der erste und der zweite Parameter waren, um die Optionen korrekt zu erstellen.
Jetzt unterstützen wir nur noch das Objektformat.
useQuery(key, fn, options) // [!code --]
useQuery({ queryKey, queryFn, ...options }) // [!code ++]
useInfiniteQuery(key, fn, options) // [!code --]
useInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
useMutation(fn, options) // [!code --]
useMutation({ mutationFn, ...options }) // [!code ++]
useIsFetching(key, filters) // [!code --]
useIsFetching({ queryKey, ...filters }) // [!code ++]
useIsMutating(key, filters) // [!code --]
useIsMutating({ mutationKey, ...filters }) // [!code ++]
useQuery(key, fn, options) // [!code --]
useQuery({ queryKey, queryFn, ...options }) // [!code ++]
useInfiniteQuery(key, fn, options) // [!code --]
useInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
useMutation(fn, options) // [!code --]
useMutation({ mutationFn, ...options }) // [!code ++]
useIsFetching(key, filters) // [!code --]
useIsFetching({ queryKey, ...filters }) // [!code ++]
useIsMutating(key, filters) // [!code --]
useIsMutating({ mutationKey, ...filters }) // [!code ++]
queryClient.isFetching(key, filters) // [!code --]
queryClient.isFetching({ queryKey, ...filters }) // [!code ++]
queryClient.ensureQueryData(key, filters) // [!code --]
queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++]
queryClient.getQueriesData(key, filters) // [!code --]
queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++]
queryClient.setQueriesData(key, updater, filters, options) // [!code --]
queryClient.setQueriesData({ queryKey, ...filters }, updater, options) // [!code ++]
queryClient.removeQueries(key, filters) // [!code --]
queryClient.removeQueries({ queryKey, ...filters }) // [!code ++]
queryClient.resetQueries(key, filters, options) // [!code --]
queryClient.resetQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.cancelQueries(key, filters, options) // [!code --]
queryClient.cancelQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.invalidateQueries(key, filters, options) // [!code --]
queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.refetchQueries(key, filters, options) // [!code --]
queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.fetchQuery(key, fn, options) // [!code --]
queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchQuery(key, fn, options) // [!code --]
queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.fetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.isFetching(key, filters) // [!code --]
queryClient.isFetching({ queryKey, ...filters }) // [!code ++]
queryClient.ensureQueryData(key, filters) // [!code --]
queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++]
queryClient.getQueriesData(key, filters) // [!code --]
queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++]
queryClient.setQueriesData(key, updater, filters, options) // [!code --]
queryClient.setQueriesData({ queryKey, ...filters }, updater, options) // [!code ++]
queryClient.removeQueries(key, filters) // [!code --]
queryClient.removeQueries({ queryKey, ...filters }) // [!code ++]
queryClient.resetQueries(key, filters, options) // [!code --]
queryClient.resetQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.cancelQueries(key, filters, options) // [!code --]
queryClient.cancelQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.invalidateQueries(key, filters, options) // [!code --]
queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.refetchQueries(key, filters, options) // [!code --]
queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.fetchQuery(key, fn, options) // [!code --]
queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchQuery(key, fn, options) // [!code --]
queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.fetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryCache.find(key, filters) // [!code --]
queryCache.find({ queryKey, ...filters }) // [!code ++]
queryCache.findAll(key, filters) // [!code --]
queryCache.findAll({ queryKey, ...filters }) // [!code ++]
queryCache.find(key, filters) // [!code --]
queryCache.find({ queryKey, ...filters }) // [!code ++]
queryCache.findAll(key, filters) // [!code --]
queryCache.findAll({ queryKey, ...filters }) // [!code ++]
Das Argument queryClient.getQueryData wurde geändert, um nur einen queryKey zu akzeptieren
queryClient.getQueryData(queryKey, filters) // [!code --]
queryClient.getQueryData(queryKey) // [!code ++]
queryClient.getQueryData(queryKey, filters) // [!code --]
queryClient.getQueryData(queryKey) // [!code ++]
Das Argument queryClient.getQueryState wurde geändert, um nur einen queryKey zu akzeptieren
queryClient.getQueryState(queryKey, filters) // [!code --]
queryClient.getQueryState(queryKey) // [!code ++]
queryClient.getQueryState(queryKey, filters) // [!code --]
queryClient.getQueryState(queryKey) // [!code ++]
Um die Migration von Remove-Überladungen zu erleichtern, bietet v5 ein Codemod.
Der Codemod ist ein Best-Efforts-Versuch, Ihnen bei der Migration von Breaking Changes zu helfen. Bitte überprüfen Sie den generierten Code gründlich! Außerdem gibt es Randfälle, die der Code-Mod nicht finden kann, also achten Sie bitte auf die Log-Ausgabe.
Wenn Sie ihn auf Dateien mit der Endung .js oder .jsx anwenden möchten, verwenden Sie den folgenden Befehl
npx jscodeshift@latest ./path/to/src/ \
--extensions=js,jsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
npx jscodeshift@latest ./path/to/src/ \
--extensions=js,jsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
Wenn Sie ihn auf Dateien mit der Endung .ts oder .tsx anwenden möchten, verwenden Sie den folgenden Befehl
npx jscodeshift@latest ./path/to/src/ \
--extensions=ts,tsx \
--parser=tsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
npx jscodeshift@latest ./path/to/src/ \
--extensions=ts,tsx \
--parser=tsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
Bitte beachten Sie, dass Sie im Fall von TypeScript tsx als Parser verwenden müssen, andernfalls wird der Codemod nicht richtig angewendet!
Hinweis: Die Anwendung des Codemods kann Ihre Codeformatierung beeinträchtigen. Vergessen Sie also nicht, prettier und/oder eslint auszuführen, nachdem Sie den Codemod angewendet haben!
Ein paar Hinweise, wie der Codemod funktioniert
onSuccess, onError und onSettled wurden aus Queries entfernt. Sie wurden für Mutations nicht berührt. Bitte siehe dieses RFC für die Motivationen hinter dieser Änderung und was stattdessen zu tun ist.
Dies optimiert die Aufrufe von Callbacks (die Callbacks refetchOnWindowFocus, refetchOnMount und refetchOnReconnect erhalten ebenfalls nur die Query übergeben) und behebt einige Tippfehlerprobleme, wenn Callbacks Daten erhalten, die durch select transformiert wurden.
- refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false | undefined) // [!code --]
+ refetchInterval: number | false | ((query: Query) => number | false | undefined) // [!code ++]
- refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false | undefined) // [!code --]
+ refetchInterval: number | false | ((query: Query) => number | false | undefined) // [!code ++]
Sie können immer noch auf Daten über query.state.data zugreifen, allerdings werden dies nicht die durch select transformierten Daten sein. Wenn Sie auf die transformierten Daten zugreifen möchten, können Sie die Transformation erneut auf query.state.data anwenden.
Zuvor entfernte die remove-Methode die Query aus der queryCache, ohne Beobachter darüber zu informieren. Sie wurde am besten verwendet, um Daten imperativ zu entfernen, die nicht mehr benötigt wurden, z. B. beim Abmelden eines Benutzers.
Aber es macht nicht viel Sinn, dies zu tun, während eine Abfrage noch aktiv ist, da dies bei der nächsten Neurendung nur einen harten Ladezustand auslöst.
Wenn Sie immer noch eine Abfrage entfernen müssen, können Sie queryClient.removeQueries({queryKey: key}) verwenden
const queryClient = useQueryClient()
const query = useQuery({ queryKey, queryFn })
query.remove() // [!code --]
queryClient.removeQueries({ queryKey }) // [!code ++]
const queryClient = useQueryClient()
const query = useQuery({ queryKey, queryFn })
query.remove() // [!code --]
queryClient.removeQueries({ queryKey }) // [!code ++]
Hauptsächlich, weil ein wichtiger Fix in Bezug auf die Typinferenz ausgeliefert wurde. Bitte siehe dieses TypeScript-Problem für weitere Informationen.
Zuvor wurde diese Funktion verwendet, um anzugeben, ob frühere Daten (true) oder neue Daten (false) als aufgelöste Daten für die Abfrage verwendet werden sollten.
Sie können dieselbe Funktionalität erreichen, indem Sie stattdessen eine Funktion an structuralSharing übergeben
import { replaceEqualDeep } from '@tanstack/react-query'
- isDataEqual: (oldData, newData) => customCheck(oldData, newData) // [!code --]
+ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) // [!code ++]
import { replaceEqualDeep } from '@tanstack/react-query'
- isDataEqual: (oldData, newData) => customCheck(oldData, newData) // [!code --]
+ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) // [!code ++]
Benutzerdefinierte Logger waren bereits in Version 4 als veraltet markiert und wurden in dieser Version entfernt. Das Logging hatte nur Auswirkungen im Entwicklungsmodus, wo die Übergabe eines benutzerdefinierten Loggers nicht notwendig ist.
Wir haben unseren browserslist aktualisiert, um ein moderneres, performanteres und kleineres Bundle zu erzeugen. Sie können die Anforderungen hier lesen.
TanStack Query hatte schon immer private Felder und Methoden in Klassen, aber sie waren nicht wirklich privat - sie waren nur in TypeScript privat. Wir verwenden jetzt ECMAScript Private Class Features, was bedeutet, dass diese Felder jetzt wirklich privat sind und zur Laufzeit nicht von außen zugänglich sind.
cacheTime umbenannt in gcTimeFast jeder versteht cacheTime falsch. Es klingt wie "die Zeitspanne, für die Daten im Cache gespeichert werden", aber das ist nicht korrekt.
cacheTime tut nichts, solange eine Abfrage noch in Benutzung ist. Sie tritt erst in Kraft, sobald die Abfrage nicht mehr genutzt wird. Nach Ablauf der Zeit werden die Daten "garbage collected", um zu verhindern, dass der Cache wächst.
gc bezieht sich auf die "Garbage Collect"-Zeit. Es ist etwas technischer, aber auch eine ziemlich bekannte Abkürzung in der Informatik.
const MINUTE = 1000 * 60;
const queryClient = new QueryClient({
defaultOptions: {
queries: {
- cacheTime: 10 * MINUTE, // [!code --]
+ gcTime: 10 * MINUTE, // [!code ++]
},
},
})
const MINUTE = 1000 * 60;
const queryClient = new QueryClient({
defaultOptions: {
queries: {
- cacheTime: 10 * MINUTE, // [!code --]
+ gcTime: 10 * MINUTE, // [!code ++]
},
},
})
Um die Option useErrorBoundary Framework-agnostischer zu gestalten und Verwechslungen mit dem etablierten React-Funktionspräfix "use" für Hooks und dem Komponentennamen "ErrorBoundary" zu vermeiden, wurde sie in throwOnError umbenannt, um ihre Funktionalität genauer wiederzugeben.
Auch wenn man in JavaScript alles throwen kann (was unknown zum korrektesten Typ macht), werden fast immer Errors (oder Unterklassen von Error) geworfen. Diese Änderung erleichtert in den meisten Fällen die Arbeit mit dem error-Feld in TypeScript.
Wenn Sie etwas werfen möchten, das kein Fehler ist, müssen Sie jetzt den Generics selbst festlegen
useQuery<number, string>({
queryKey: ['some-query'],
queryFn: async () => {
if (Math.random() > 0.5) {
throw 'some error'
}
return 42
},
})
useQuery<number, string>({
queryKey: ['some-query'],
queryFn: async () => {
if (Math.random() > 0.5) {
throw 'some error'
}
return 42
},
})
Eine Möglichkeit, einen anderen Fehlertyp global festzulegen, finden Sie im TypeScript-Leitfaden.
Da nun nur noch die Objekt-Syntax unterstützt wird, ist diese Regel nicht mehr erforderlich
keepPreviousData entfernt zugunsten der Identitätsfunktion placeholderDataWir haben die Option keepPreviousData und das Flag isPreviousData entfernt, da sie größtenteils dasselbe taten wie placeholderData und das Flag isPlaceholderData.
Um die gleiche Funktionalität wie keepPreviousData zu erreichen, haben wir die previous query data als Argument zu placeholderData hinzugefügt, das eine Identitätsfunktion akzeptiert. Daher müssen Sie nur eine Identitätsfunktion an placeholderData übergeben oder die mitgelieferte keepPreviousData-Funktion von Tanstack Query verwenden.
Ein Hinweis hier ist, dass useQueries previousData in der placeholderData-Funktion nicht als Argument erhält. Dies liegt an der dynamischen Natur der übergebenen Abfragen im Array, was zu einer anderen Form des Ergebnisses von placeholder und queryFn führen kann.
import {
useQuery,
+ keepPreviousData // [!code ++]
} from "@tanstack/react-query";
const {
data,
- isPreviousData, // [!code --]
+ isPlaceholderData, // [!code ++]
} = useQuery({
queryKey,
queryFn,
- keepPreviousData: true, // [!code --]
+ placeholderData: keepPreviousData // [!code ++]
});
import {
useQuery,
+ keepPreviousData // [!code ++]
} from "@tanstack/react-query";
const {
data,
- isPreviousData, // [!code --]
+ isPlaceholderData, // [!code ++]
} = useQuery({
queryKey,
queryFn,
- keepPreviousData: true, // [!code --]
+ placeholderData: keepPreviousData // [!code ++]
});
Eine Identitätsfunktion bezieht sich im Kontext von Tanstack Query auf eine Funktion, die immer ihr bereitgestelltes Argument (d.h. Daten) unverändert zurückgibt.
useQuery({
queryKey,
queryFn,
placeholderData: (previousData, previousQuery) => previousData, // identity function with the same behaviour as `keepPreviousData`
})
useQuery({
queryKey,
queryFn,
placeholderData: (previousData, previousQuery) => previousData, // identity function with the same behaviour as `keepPreviousData`
})
Es gibt jedoch einige Vorbehalte bei dieser Änderung, die Sie beachten müssen
placeholderData versetzt Sie immer in den success-Status, während keepPreviousData Ihnen den Status der vorherigen Abfrage gab. Dieser Status könnte error sein, wenn wir erfolgreich Daten abgerufen haben und dann einen Fehler bei der Hintergrundaktualisierung erhalten haben. Der Fehler selbst wurde jedoch nicht geteilt, daher haben wir uns entschieden, beim Verhalten von placeholderData zu bleiben.
keepPreviousData gab Ihnen den dataUpdatedAt-Zeitstempel der vorherigen Daten zurück, während bei placeholderData dataUpdatedAt bei 0 bleibt. Dies kann ärgerlich sein, wenn Sie diesen Zeitstempel kontinuierlich auf dem Bildschirm anzeigen möchten. Sie können dies jedoch mit useEffect umgehen.
const [updatedAt, setUpdatedAt] = useState(0)
const { data, dataUpdatedAt } = useQuery({
queryKey: ['projects', page],
queryFn: () => fetchProjects(page),
})
useEffect(() => {
if (dataUpdatedAt > updatedAt) {
setUpdatedAt(dataUpdatedAt)
}
}, [dataUpdatedAt])
const [updatedAt, setUpdatedAt] = useState(0)
const { data, dataUpdatedAt } = useQuery({
queryKey: ['projects', page],
queryFn: () => fetchProjects(page),
})
useEffect(() => {
if (dataUpdatedAt > updatedAt) {
setUpdatedAt(dataUpdatedAt)
}
}, [dataUpdatedAt])
Das visibilitychange-Ereignis wird ausschließlich verwendet. Dies ist möglich, da wir nur Browser unterstützen, die das visibilitychange-Ereignis unterstützen. Dies behebt eine Reihe von Problemen, wie hier aufgeführt.
navigator.onLine funktioniert in Chromium-basierten Browsern nicht gut. Es gibt viele Probleme mit falschen Negativen, die dazu führen, dass Abfragen fälschlicherweise als offline markiert werden.
Um dies zu umgehen, beginnen wir nun immer mit online: true und lauschen nur auf online und offline Ereignisse, um den Status zu aktualisieren.
Dies sollte die Wahrscheinlichkeit von falschen Negativen verringern, kann aber zu falschen Positiven für Offline-Anwendungen führen, die über ServiceWorker geladen werden und auch ohne Internetverbindung funktionieren können.
In v4 haben wir die Möglichkeit eingeführt, benutzerdefinierten context an alle react-query-Hooks zu übergeben. Dies ermöglichte eine korrekte Isolierung bei der Verwendung von MicroFrontends.
context ist jedoch eine reine React-Funktion. Alles, was context tut, ist uns den Zugriff auf den queryClient zu ermöglichen. Diesen gleichen Grad an Isolierung könnten wir erreichen, indem wir erlauben, einen benutzerdefinierten queryClient direkt zu übergeben. Dies wiederum wird es anderen Frameworks ermöglichen, die gleiche Funktionalität auf eine Framework-unabhängige Weise zu erhalten.
import { queryClient } from './my-client'
const { data } = useQuery(
{
queryKey: ['users', id],
queryFn: () => fetch(...),
- context: customContext // [!code --]
},
+ queryClient, // [!code ++]
)
import { queryClient } from './my-client'
const { data } = useQuery(
{
queryKey: ['users', id],
queryFn: () => fetch(...),
- context: customContext // [!code --]
},
+ queryClient, // [!code ++]
)
refetchPage entfernt zugunsten von maxPagesIn v4 haben wir die Möglichkeit eingeführt, die Seiten für unendliche Abfragen mit der Funktion refetchPage zu definieren.
Das erneute Abrufen aller Seiten kann jedoch zu UI-Inkonsistenzen führen. Außerdem ist diese Option z.B. bei queryClient.refetchQueries verfügbar, aber sie tut nur etwas für unendliche Abfragen, nicht für "normale" Abfragen.
Die v5 enthält eine neue Option maxPages für unendliche Abfragen, um die Anzahl der im Abfragedaten gespeicherten und anschließend abgerufenen Seiten zu begrenzen. Diese neue Funktion deckt die Anwendungsfälle ab, die ursprünglich für die refetchPage-Seitenfunktion identifiziert wurden, ohne die damit verbundenen Probleme.
Die Optionen, die an dehydrate übergeben werden können, wurden vereinfacht. Abfragen und Mutationen werden immer dehydriert (gemäß der Standardfunktionsimplementierung). Um dieses Verhalten zu ändern, können Sie anstelle der entfernten booleschen Optionen dehydrateMutations und dehydrateQueries stattdessen die Funktionsäquivalente shouldDehydrateQuery oder shouldDehydrateMutation implementieren. Um das alte Verhalten, Abfragen/Mutationen gar nicht zu hydrieren, zu erhalten, übergeben Sie () => false.
- dehydrateMutations?: boolean // [!code --]
- dehydrateQueries?: boolean // [!code --]
- dehydrateMutations?: boolean // [!code --]
- dehydrateQueries?: boolean // [!code --]
Zuvor haben wir undefined an die queryFn als pageParam übergeben, und Sie konnten einen Standardwert dem pageParam-Parameter in der queryFn-Funktionssignatur zuweisen. Dies hatte den Nachteil, undefined im queryCache zu speichern, was nicht serialisierbar ist.
Stattdessen müssen Sie jetzt einen expliziten initialPageParam an die Optionen für unendliche Abfragen übergeben. Dieser wird als pageParam für die erste Seite verwendet
useInfiniteQuery({
queryKey,
- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam), // [!code --]
+ queryFn: ({ pageParam }) => fetchSomething(pageParam), // [!code ++]
+ initialPageParam: 0, // [!code ++]
getNextPageParam: (lastPage) => lastPage.next,
})
useInfiniteQuery({
queryKey,
- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam), // [!code --]
+ queryFn: ({ pageParam }) => fetchSomething(pageParam), // [!code ++]
+ initialPageParam: 0, // [!code ++]
getNextPageParam: (lastPage) => lastPage.next,
})
Zuvor erlaubten wir es, die von getNextPageParam oder getPreviousPageParam zurückgegebenen pageParams zu überschreiben, indem wir einen pageParam-Wert direkt an fetchNextPage oder fetchPreviousPage übergeben. Diese Funktion funktionierte bei Refetches überhaupt nicht und war nicht weithin bekannt oder genutzt. Dies bedeutet auch, dass getNextPageParam jetzt für unendliche Abfragen erforderlich ist.
In v4 mussten Sie explizit undefined zurückgeben, um anzuzeigen, dass keine weitere Seite verfügbar ist. Wir haben diese Prüfung erweitert, um null einzuschließen.
Auf dem Server ist retry jetzt standardmäßig 0 statt 3. Für Prefetching haben wir immer auf 0 Wiederholungsversuche gesetzt, aber da Abfragen mit suspense nun auch auf dem Server direkt ausgeführt werden können (seit React18), müssen wir sicherstellen, dass auf dem Server überhaupt keine Wiederholungsversuche stattfinden.
status: loading wurde zu status: pending geändert und isLoading wurde zu isPending geändert und isInitialLoading wurde jetzt in isLoading umbenanntDer Status loading wurde in pending umbenannt, und ähnlich wurde das abgeleitete Flag isLoading in isPending umbenannt.
Auch für Mutationen wurde der status von loading zu pending geändert und das isLoading-Flag wurde zu isPending geändert.
Zuletzt wurde ein neues abgeleitetes Flag isLoading zu den Abfragen hinzugefügt, das als isPending && isFetching implementiert ist. Das bedeutet, dass isLoading und isInitialLoading dasselbe tun, aber isInitialLoading jetzt veraltet ist und in der nächsten Hauptversion entfernt wird.
Um die Gründe für diese Änderung zu verstehen, siehe die v5 Roadmap-Diskussion.
hashQueryKey wurde in hashKey umbenanntweil es auch Mutationsschlüssel hashd und innerhalb der predicate-Funktionen von useIsMutating und useMutationState verwendet werden kann, die Mutationen übergeben bekommen.
Um die Kompatibilität mit Vue 2 zu gewährleisten, gibt das useQueries-Composable jetzt das queries-Array, das in ref verpackt ist, zurück. Zuvor wurde reactive zurückgegeben, was zu mehreren Problemen führte
Mit dieser Änderung sind all diese Probleme behoben.
Außerdem richtet dies useQueries an anderen Composables aus, die alle Werte als refs zurückgeben.
Um neue Funktionen nach den Vue-Releases bereitstellen zu können, benötigen wir jetzt mindestens Vue 3 in der Version v3.3. Die Anforderungen für Vue 2.x bleiben unverändert.
v5 kommt auch mit neuen Funktionen
Wir haben eine neue, vereinfachte Methode zur Durchführung optimistischer Updates durch die Nutzung der zurückgegebenen variables von useMutation
const queryInfo = useTodos()
const addTodoMutation = useMutation({
mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }),
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
})
if (queryInfo.data) {
return (
<ul>
{queryInfo.data.items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
{addTodoMutation.isPending && (
<li key={String(addTodoMutation.submittedAt)} style={{ opacity: 0.5 }}>
{addTodoMutation.variables}
</li>
)}
</ul>
)
}
const queryInfo = useTodos()
const addTodoMutation = useMutation({
mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }),
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
})
if (queryInfo.data) {
return (
<ul>
{queryInfo.data.items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
{addTodoMutation.isPending && (
<li key={String(addTodoMutation.submittedAt)} style={{ opacity: 0.5 }}>
{addTodoMutation.variables}
</li>
)}
</ul>
)
}
Hier ändern wir nur, wie die Benutzeroberfläche aussieht, wenn die Mutation ausgeführt wird, anstatt Daten direkt in den Cache zu schreiben. Dies funktioniert am besten, wenn wir nur einen Ort haben, an dem wir die optimistische Aktualisierung anzeigen müssen. Weitere Details finden Sie in der Dokumentation zu optimistic updates.
Unendliche Abfragen sind großartig, wenn unendliches Scrollen oder Paginierung benötigt wird. Je mehr Seiten Sie abrufen, desto mehr Speicher verbrauchen Sie, und dies verlangsamt auch den Prozess des erneuten Abrufens von Abfragen, da alle Seiten sequenziell erneut abgerufen werden.
Version 5 verfügt über eine neue Option maxPages für unendliche Abfragen, mit der Entwickler die Anzahl der Seiten begrenzen können, die in den Abfragedaten gespeichert und anschließend erneut abgerufen werden. Sie können den maxPages-Wert an die gewünschte UX und die Leistung beim erneuten Abrufen anpassen.
Beachten Sie, dass die unendliche Liste bidirektional sein muss, was erfordert, dass sowohl getNextPageParam als auch getPreviousPageParam definiert sind.
Unendliche Abfragen können wie normale Abfragen vorab abgerufen werden. Standardmäßig wird nur die erste Seite der Abfrage vorab abgerufen und unter dem angegebenen QueryKey gespeichert. Wenn Sie mehr als eine Seite vorab abrufen möchten, können Sie die Option pages verwenden. Lesen Sie den Leitfaden Prefetching für weitere Informationen.
Weitere Details finden Sie in der useQueries-Dokumentation.
Weitere Details finden Sie in der Dokumentation zu experimental_createPersister.
Zuvor konnten vue-query-Composables nur innerhalb der setup-Funktion der Komponente ausgeführt werden.
Wir hatten eine Ausweichmöglichkeit, um diese Hooks überall ausführen zu lassen, wenn der Benutzer queryClient als Composable-Option bereitstellte.
Jetzt können Sie vue-query-Composables in jeder Funktion verwenden, die injectionContext unterstützt. z.B. Router-Navigationswächter. Wenn Sie diese neue Funktion verwenden, stellen Sie sicher, dass das vue-query-Composable innerhalb eines effectScope ausgeführt wird. Andernfalls kann dies zu Speicherlecks führen. Wir haben dev-only-Warnungen hinzugefügt, um Benutzer über mögliche Fehlbenutzungen zu informieren.