QueryCache wurde in eine QueryClient und niedrigere Instanzen von QueryCache und MutationCache aufgeteilt.ReactQueryConfigProvider und ReactQueryCacheProvider wurden beide durch QueryClientProvider ersetzt.QueryCache ist weg. Diesmal wirklich!makeQueryCache wurde entfernt.QueryCache.prefetchQuery() wurde zu QueryClient.prefetchQuery() verschoben.ReactQueryErrorResetBoundary und QueryCache.resetErrorBoundaries() wurden durch QueryErrorResetBoundary und useQueryErrorResetBoundary() ersetzt.QueryCache.getQuery() wurde durch QueryCache.find() ersetzt.QueryCache.getQueries() wurde zu QueryCache.findAll() verschoben.QueryCache.isFetching wurde zu QueryClient.isFetching() verschoben.useQueryCache wurde durch den Hook useQueryClient ersetzt.QueryFunctionContext.pageParam übergeben.keepPreviousData entfernt.mutation.mutate gibt kein Promise mehr zurück.true/false).QueryOptions.forceFetchOnMount wurde durch refetchOnMount: 'always' ersetzt.QueryOptions.refetchOnMount gelten nun nur noch für die übergeordnete Komponente und nicht mehr für alle Query-Observer.QueryOptions.queryFnParamsFilter wurde zugunsten des neuen Objekts QueryFunctionContext entfernt.QueryOptions.notifyOnStatusChange wurde durch die neuen Optionen notifyOnChangeProps und notifyOnChangePropsExclusions abgelöst.QueryResult.clear() wurde in QueryResult.remove() umbenannt.QueryResult.updatedAt wurde in die Eigenschaften QueryResult.dataUpdatedAt und QueryResult.errorUpdatedAt aufgeteilt.setConsole() wurde durch die neue Funktion setLogger() ersetzt.QueryStatus wurde von einem Enum in einen Union-Typ geändertFrühere Versionen von React Query waren großartig und brachten einige erstaunliche neue Features, mehr Magie und ein insgesamt besseres Erlebnis für die Bibliothek. Sie brachten auch massive Adoption und damit viele verfeinernde Einflüsse (Probleme/Beiträge) für die Bibliothek und brachten einige Dinge ans Licht, die mehr Politur brauchten, um die Bibliothek noch besser zu machen. v3 enthält genau diese Politur.
Die QueryCache enthält alle Queries, die MutationCache enthält alle Mutationen, und die QueryClient kann verwendet werden, um Konfigurationen festzulegen und mit ihnen zu interagieren.
Dies hat einige Vorteile
Beim Erstellen eines new QueryClient() werden automatisch eine QueryCache und eine MutationCache für Sie erstellt, wenn Sie keine angeben.
import { QueryClient } from 'react-query'
const queryClient = new QueryClient()
import { QueryClient } from 'react-query'
const queryClient = new QueryClient()
Standardoptionen für Queries und Mutationen können jetzt in QueryClient angegeben werden.
Beachten Sie, dass es sich jetzt um defaultOptions anstelle von defaultConfig handelt.
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// query options
},
mutations: {
// mutation options
},
},
})
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// query options
},
mutations: {
// mutation options
},
},
})
Die Komponente QueryClientProvider wird nun verwendet, um einen QueryClient mit Ihrer Anwendung zu verbinden.
import { QueryClient, QueryClientProvider } from 'react-query'
const queryClient = new QueryClient()
function App() {
return <QueryClientProvider client={queryClient}>...</QueryClientProvider>
}
import { QueryClient, QueryClientProvider } from 'react-query'
const queryClient = new QueryClient()
function App() {
return <QueryClientProvider client={queryClient}>...</QueryClientProvider>
}
QueryCache ist weg. Diesmal wirklich!Wie bereits mit einer Deprecation angemerkt, gibt es keine Standard-QueryCache mehr, die aus dem Hauptpaket erstellt oder exportiert wird. Sie müssen Ihre eigene über new QueryClient() oder new QueryCache() (die Sie dann an new QueryClient({ queryCache }) übergeben können) erstellen.
Es hat lange gedauert, aber jetzt ist es endlich weg :)
Die neue Funktion QueryClient.prefetchQuery() ist asynchron, gibt aber nicht die Daten aus der Query zurück. Wenn Sie die Daten benötigen, verwenden Sie die neue Funktion QueryClient.fetchQuery().
// Prefetch a query:
await queryClient.prefetchQuery('posts', fetchPosts)
// Fetch a query:
try {
const data = await queryClient.fetchQuery('posts', fetchPosts)
} catch (error) {
// Error handling
}
// Prefetch a query:
await queryClient.prefetchQuery('posts', fetchPosts)
// Fetch a query:
try {
const data = await queryClient.fetchQuery('posts', fetchPosts)
} catch (error) {
// Error handling
}
Zusammen bieten diese die gleiche Erfahrung wie zuvor, jedoch mit zusätzlicher Kontrolle, um auszuwählen, welche Komponentenbäume zurückgesetzt werden sollen. Weitere Informationen finden Sie unter
QueryCache.getQuery() wurde durch QueryCache.find() ersetzt.QueryCache.find() sollte nun verwendet werden, um einzelne Queries aus einem Cache nachzuschlagen.
QueryCache.getQueries() wurde zu QueryCache.findAll() verschoben.QueryCache.findAll() sollte nun verwendet werden, um mehrere Queries aus einem Cache nachzuschlagen.
QueryCache.isFetching wurde zu QueryClient.isFetching() verschoben.Beachten Sie, dass es sich jetzt um eine Funktion anstelle einer Eigenschaft handelt.
useQueryCache wurde durch den Hook useQueryClient ersetzt.Er gibt den bereitgestellten queryClient für seinen Komponententeilbaum zurück und sollte nicht viel mehr als eine Umbenennung erfordern.
Inline-Funktionen sind jetzt der empfohlene Weg, um Parameter an Ihre Query-Funktionen zu übergeben.
// Old
useQuery(['post', id], (_key, id) => fetchPost(id))
// New
useQuery(['post', id], () => fetchPost(id))
// Old
useQuery(['post', id], (_key, id) => fetchPost(id))
// New
useQuery(['post', id], () => fetchPost(id))
Wenn Sie immer noch darauf bestehen, keine Inline-Funktionen zu verwenden, können Sie die neu übergebene QueryFunctionContext verwenden.
useQuery(['post', id], (context) => fetchPost(context.queryKey[1]))
useQuery(['post', id], (context) => fetchPost(context.queryKey[1]))
Sie wurden zuvor als letzter Query-Key-Parameter in Ihrer Query-Funktion hinzugefügt, aber dies erwies sich für einige Muster als schwierig.
// Old
useInfiniteQuery(['posts'], (_key, pageParam = 0) => fetchPosts(pageParam))
// New
useInfiniteQuery(['posts'], ({ pageParam = 0 }) => fetchPosts(pageParam))
// Old
useInfiniteQuery(['posts'], (_key, pageParam = 0) => fetchPosts(pageParam))
// New
useInfiniteQuery(['posts'], ({ pageParam = 0 }) => fetchPosts(pageParam))
Die neue Option keepPreviousData ist sowohl für useQuery als auch für useInfiniteQuery verfügbar und hat den gleichen "verzögernden" Effekt auf Ihre Daten.
import { useQuery } from 'react-query'
function Page({ page }) {
const { data } = useQuery(['page', page], fetchPage, {
keepPreviousData: true,
})
}
import { useQuery } from 'react-query'
function Page({ page }) {
const { data } = useQuery(['page', page], fetchPage, {
keepPreviousData: true,
})
}
Die Benutzeroberfläche von useInfiniteQuery() wurde geändert, um bidirektionale unendliche Listen vollständig zu unterstützen.
Eine Richtung
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
)
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
)
Beide Richtungen
const {
data,
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
} = useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor,
},
)
const {
data,
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
} = useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor,
},
)
Eine Richtung umgekehrt
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
select: (data) => ({
pages: [...data.pages].reverse(),
pageParams: [...data.pageParams].reverse(),
}),
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
)
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
select: (data) => ({
pages: [...data.pages].reverse(),
pageParams: [...data.pageParams].reverse(),
}),
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
)
Dies ermöglicht eine einfachere Manipulation der Daten und der Seitenparameter, wie z. B. das Entfernen der ersten Datenseite zusammen mit ihren Parametern.
queryClient.setQueryData(['projects'], (data) => ({
pages: data.pages.slice(1),
pageParams: data.pageParams.slice(1),
}))
queryClient.setQueryData(['projects'], (data) => ({
pages: data.pages.slice(1),
pageParams: data.pageParams.slice(1),
}))
Obwohl der alte Weg uns warme, wohlige Gefühle gab, als wir useState zum ersten Mal entdeckten, hielten diese nicht lange an. Jetzt ist die Mutationsrückgabe ein einzelnes Objekt.
// Old:
const [mutate, { status, reset }] = useMutation()
// New:
const { mutate, status, reset } = useMutation()
// Old:
const [mutate, { status, reset }] = useMutation()
// New:
const { mutate, status, reset } = useMutation()
mutation.mutate gibt kein Promise mehr zurück.Wir erhielten viele Fragen zu diesem Verhalten, da Benutzer erwarteten, dass das Promise sich wie ein reguläres Promise verhält.
Aus diesem Grund wurde die Funktion mutate in eine mutate und eine mutateAsync Funktion aufgeteilt.
Die Funktion mutate kann bei der Verwendung von Callbacks verwendet werden.
const { mutate } = useMutation({ mutationFn: addTodo })
mutate('todo', {
onSuccess: (data) => {
console.log(data)
},
onError: (error) => {
console.error(error)
},
onSettled: () => {
console.log('settled')
},
})
const { mutate } = useMutation({ mutationFn: addTodo })
mutate('todo', {
onSuccess: (data) => {
console.log(data)
},
onError: (error) => {
console.error(error)
},
onSettled: () => {
console.log('settled')
},
})
Die Funktion mutateAsync kann bei der Verwendung von async/await verwendet werden.
const { mutateAsync } = useMutation({ mutationFn: addTodo })
try {
const data = await mutateAsync('todo')
console.log(data)
} catch (error) {
console.error(error)
} finally {
console.log('settled')
}
const { mutateAsync } = useMutation({ mutationFn: addTodo })
try {
const data = await mutateAsync('todo')
console.log(data)
} catch (error) {
console.error(error)
} finally {
console.log('settled')
}
// Old:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
config: { staleTime: Infinity },
})
// New:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
staleTime: Infinity,
})
// Old:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
config: { staleTime: Infinity },
})
// New:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
staleTime: Infinity,
})
false).Die Query-Option enabled deaktiviert eine Query nun nur noch, wenn der Wert false ist. Bei Bedarf können Werte mit !!userId oder Boolean(userId) umgewandelt werden, und es wird ein hilfreicher Fehler ausgelöst, wenn ein nicht-booleanischer Wert übergeben wird.
Die Option initialStale wurde entfernt und initiale Daten werden nun als normale Daten behandelt. Das bedeutet, wenn initialData bereitgestellt wird, wird die Query beim Mounten standardmäßig neu abgerufen. Wenn Sie nicht sofort neu abrufen möchten, können Sie eine staleTime definieren.
Ehrlich gesagt, wir hatten viel zu viele refetchOn____ Optionen, also sollte dies für Klarheit sorgen.
Wenn refetchOnMount auf false gesetzt war, wurde verhindert, dass zusätzliche Komponenten beim Mounten neu abgerufen wurden. In Version 3 wird nur die Komponente, in der die Option gesetzt wurde, nicht erneut abgerufen.
QueryOptions.queryFnParamsFilter wurde zugunsten des neuen Objekts QueryFunctionContext entfernt.Die Option queryFnParamsFilter wurde entfernt, da Query-Funktionen nun ein QueryFunctionContext-Objekt anstelle des Query-Schlüssels erhalten.
Parameter können immer noch innerhalb der Query-Funktion selbst gefiltert werden, da die QueryFunctionContext auch den Query-Schlüssel enthält.
Mit diesen neuen Optionen ist es möglich, granular zu konfigurieren, wann eine Komponente neu rendern soll.
Nur neu rendern, wenn sich die Eigenschaften data oder error ändern.
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangeProps: ['data', 'error'],
})
return <div>Username: {data.username}</div>
}
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangeProps: ['data', 'error'],
})
return <div>Username: {data.username}</div>
}
Verhindern Sie das erneute Rendern, wenn sich die Eigenschaft isStale ändert.
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangePropsExclusions: ['isStale'],
})
return <div>Username: {data.username}</div>
}
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangePropsExclusions: ['isStale'],
})
return <div>Username: {data.username}</div>
}
Obwohl sie clear hieß, entfernte sie lediglich die Query aus dem Cache. Der Name entspricht nun der Funktionalität.
Da Daten und Fehler gleichzeitig vorhanden sein können, wurde die Eigenschaft updatedAt in dataUpdatedAt und errorUpdatedAt aufgeteilt.
setConsole() wurde durch die neue Funktion setLogger() ersetzt.import { setLogger } from 'react-query'
// Log with Sentry
setLogger({
error: (error) => {
Sentry.captureException(error)
},
})
// Log with Winston
setLogger(winston.createLogger())
import { setLogger } from 'react-query'
// Log with Sentry
setLogger({
error: (error) => {
Sentry.captureException(error)
},
})
// Log with Winston
setLogger(winston.createLogger())
Um die Anzeige von Fehlerbildschirmen in React Native zu verhindern, wenn eine Query fehlschlägt, war es notwendig, die Konsole manuell zu ändern.
import { setConsole } from 'react-query'
setConsole({
log: console.log,
warn: console.warn,
error: console.warn,
})
import { setConsole } from 'react-query'
setConsole({
log: console.log,
warn: console.warn,
error: console.warn,
})
In Version 3 geschieht dies automatisch, wenn React Query in React Native verwendet wird.
Wenn Sie also die Status-Eigenschaft einer Query oder Mutation gegen eine QueryStatus-Enum-Eigenschaft geprüft haben, müssen Sie dies nun gegen den String-Literal prüfen, den das Enum zuvor für jede Eigenschaft enthielt.
Daher müssen Sie die Enum-Eigenschaften in ihre entsprechenden String-Literale ändern, wie folgt:
Hier ist ein Beispiel für die Änderungen, die Sie vornehmen müssten:
- import { useQuery, QueryStatus } from 'react-query'; // [!code --]
+ import { useQuery } from 'react-query'; // [!code ++]
const { data, status } = useQuery(['post', id], () => fetchPost(id))
- if (status === QueryStatus.Loading) { // [!code --]
+ if (status === 'loading') { // [!code ++]
...
}
- if (status === QueryStatus.Error) { // [!code --]
+ if (status === 'error') { // [!code ++]
...
}
- import { useQuery, QueryStatus } from 'react-query'; // [!code --]
+ import { useQuery } from 'react-query'; // [!code ++]
const { data, status } = useQuery(['post', id], () => fetchPost(id))
- if (status === QueryStatus.Loading) { // [!code --]
+ if (status === 'loading') { // [!code ++]
...
}
- if (status === QueryStatus.Error) { // [!code --]
+ if (status === 'error') { // [!code ++]
...
}
Die Hooks useQuery und useInfiniteQuery haben nun eine select-Option, um Teile des Query-Ergebnisses auszuwählen oder zu transformieren.
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
select: (user) => user.username,
})
return <div>Username: {data}</div>
}
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
select: (user) => user.username,
})
return <div>Username: {data}</div>
}
Setzen Sie die Option notifyOnChangeProps auf ['data', 'error'], um nur dann neu zu rendern, wenn sich die ausgewählten Daten ändern.
Wünschen Sie sich, eine Schleife mit useQuery ausführen zu können? Die Regeln von Hooks sagen nein, aber mit dem neuen Hook useQueries() können Sie das!
import { useQueries } from 'react-query'
function Overview() {
const results = useQueries([
{ queryKey: ['post', 1], queryFn: fetchPost },
{ queryKey: ['post', 2], queryFn: fetchPost },
])
return (
<ul>
{results.map(({ data }) => data && <li key={data.id}>{data.title})</li>)}
</ul>
)
}
import { useQueries } from 'react-query'
function Overview() {
const results = useQueries([
{ queryKey: ['post', 1], queryFn: fetchPost },
{ queryKey: ['post', 2], queryFn: fetchPost },
])
return (
<ul>
{results.map(({ data }) => data && <li key={data.id}>{data.title})</li>)}
</ul>
)
}
Standardmäßig wird eine Mutation bei einem Fehler nicht wiederholt, aber es ist mit der Option retry möglich.
const mutation = useMutation({
mutationFn: addTodo,
retry: 3,
})
const mutation = useMutation({
mutationFn: addTodo,
retry: 3,
})
Wenn Mutationen fehlschlagen, weil das Gerät offline ist, werden sie in derselben Reihenfolge wiederholt, wenn das Gerät wieder online geht.
Mutationen können nun im Speicher persistiert und zu einem späteren Zeitpunkt fortgesetzt werden. Weitere Informationen finden Sie in der Mutationsdokumentation.
Ein QueryObserver kann verwendet werden, um eine Query zu erstellen und/oder zu beobachten.
const observer = new QueryObserver(queryClient, { queryKey: 'posts' })
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
const observer = new QueryObserver(queryClient, { queryKey: 'posts' })
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
Ein InfiniteQueryObserver kann verwendet werden, um eine unendliche Query zu erstellen und/oder zu beobachten.
const observer = new InfiniteQueryObserver(queryClient, {
queryKey: 'posts',
queryFn: fetchPosts,
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
})
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
const observer = new InfiniteQueryObserver(queryClient, {
queryKey: 'posts',
queryFn: fetchPosts,
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
})
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
Ein QueriesObserver kann verwendet werden, um mehrere Queries zu erstellen und/oder zu beobachten.
const observer = new QueriesObserver(queryClient, [
{ queryKey: ['post', 1], queryFn: fetchPost },
{ queryKey: ['post', 2], queryFn: fetchPost },
])
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
const observer = new QueriesObserver(queryClient, [
{ queryKey: ['post', 1], queryFn: fetchPost },
{ queryKey: ['post', 2], queryFn: fetchPost },
])
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
Die Methode QueryClient.setQueryDefaults() kann verwendet werden, um Standardoptionen für bestimmte Queries festzulegen.
queryClient.setQueryDefaults(['posts'], { queryFn: fetchPosts })
function Component() {
const { data } = useQuery(['posts'])
}
queryClient.setQueryDefaults(['posts'], { queryFn: fetchPosts })
function Component() {
const { data } = useQuery(['posts'])
}
Die Methode QueryClient.setMutationDefaults() kann verwendet werden, um Standardoptionen für bestimmte Mutationen festzulegen.
queryClient.setMutationDefaults(['addPost'], { mutationFn: addPost })
function Component() {
const { mutate } = useMutation({ mutationKey: ['addPost'] })
}
queryClient.setMutationDefaults(['addPost'], { mutationFn: addPost })
function Component() {
const { mutate } = useMutation({ mutationKey: ['addPost'] })
}
Der Hook useIsFetching() akzeptiert nun Filter, die verwendet werden können, um beispielsweise einen Spinner nur für bestimmte Arten von Queries anzuzeigen.
const fetches = useIsFetching({ queryKey: ['posts'] })
const fetches = useIsFetching({ queryKey: ['posts'] })
Der Kern von React Query ist nun vollständig von React getrennt, was bedeutet, dass er auch eigenständig oder in anderen Frameworks verwendet werden kann. Verwenden Sie den Einstiegspunkt react-query/core, um nur die Kernfunktionalität zu importieren.
import { QueryClient } from 'react-query/core'
import { QueryClient } from 'react-query/core'
Die Devtools sind nun im react-query-Paket selbst unter dem Import react-query/devtools enthalten. Ersetzen Sie einfach react-query-devtools-Importe durch react-query/devtools.