React Query wendet automatisch einige Optimierungen an, um sicherzustellen, dass Ihre Komponenten nur dann neu gerendert werden, wenn sie es tatsächlich müssen. Dies geschieht durch folgende Mittel:
React Query verwendet eine Technik namens "strukturelle Teilung" (structural sharing), um sicherzustellen, dass zwischen den Neu-Renderings so viele Referenzen wie möglich intakt bleiben. Wenn Daten über das Netzwerk abgerufen werden, erhalten Sie normalerweise durch das Parsen der Antwort als JSON eine völlig neue Referenz. React Query behält jedoch die ursprüngliche Referenz bei, wenn sich an den Daten *nichts* geändert hat. Wenn sich ein Teil geändert hat, behält React Query die unveränderten Teile bei und ersetzt nur die geänderten Teile.
Hinweis: Diese Optimierung funktioniert nur, wenn die queryFn JSON-kompatible Daten zurückgibt. Sie können sie deaktivieren, indem Sie global oder pro Abfrage structuralSharing: false setzen, oder Sie können Ihre eigene strukturelle Teilung implementieren, indem Sie eine Funktion übergeben.
Das von useQuery, useInfiniteQuery, useMutation zurückgegebene Top-Level-Objekt und das von useQueries zurückgegebene Array sind nicht referenziell stabil. Sie werden bei jedem Rendern eine neue Referenz sein. Die von diesen Hooks zurückgegebenen data-Eigenschaften sind jedoch so stabil wie möglich.
React Query löst nur dann ein erneutes Rendern aus, wenn eine der von useQuery zurückgegebenen Eigenschaften tatsächlich "verwendet" wird. Dies geschieht mithilfe eines Proxy-Objekts. Dies vermeidet viele unnötige Neu-Renderings, z. B. weil Eigenschaften wie isFetching oder isStale oft geändert werden können, aber nicht in der Komponente verwendet werden.
Sie können diese Funktion anpassen, indem Sie notifyOnChangeProps manuell global oder pro Abfrage setzen. Wenn Sie diese Funktion deaktivieren möchten, können Sie notifyOnChangeProps: 'all' setzen.
Hinweis: Der "get"-Trap eines Proxys wird aufgerufen, wenn auf eine Eigenschaft zugegriffen wird, entweder durch Destrukturierung oder durch direkten Zugriff. Wenn Sie Object Rest Destrukturierung verwenden, deaktivieren Sie diese Optimierung. Wir haben eine Lint-Regel, um Sie vor diesem Fallstrick zu schützen.
Sie können die Option select verwenden, um eine Teilmenge der Daten auszuwählen, auf die Ihre Komponente zugreifen soll. Dies ist nützlich für hochoptimierte Datentransformationen oder um unnötige Neu-Renderings zu vermeiden.
export const useTodos = (select) => {
return useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
select,
})
}
export const useTodoCount = () => {
return useTodos((data) => data.length)
}
export const useTodos = (select) => {
return useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
select,
})
}
export const useTodoCount = () => {
return useTodos((data) => data.length)
}
Eine Komponente, die den benutzerdefinierten Hook useTodoCount verwendet, wird nur dann neu gerendert, wenn sich die Länge der Todos ändert. Sie wird nicht neu gerendert, wenn sich z. B. der Name eines Todos ändert.
Hinweis: select arbeitet mit erfolgreich zwischengespeicherten Daten und ist nicht der richtige Ort, um Fehler auszulösen. Die Quelle der Wahrheit für Fehler ist die queryFn, und eine select-Funktion, die einen Fehler zurückgibt, führt dazu, dass data undefined ist und isSuccess true ist. Wir empfehlen, Fehler in der queryFn zu behandeln, wenn Sie möchten, dass eine Abfrage bei falschen Daten fehlschlägt, oder außerhalb des Abfrage-Hooks, wenn Sie einen Fehlerfall haben, der nicht mit dem Caching zusammenhängt.
Die select-Funktion wird nur neu ausgeführt, wenn
Das bedeutet, dass eine Inline-select-Funktion, wie oben gezeigt, bei jedem Rendern ausgeführt wird. Um dies zu vermeiden, können Sie die select-Funktion in useCallback einschließen oder sie in eine stabile Funktionsreferenz extrahieren, wenn sie keine Abhängigkeiten hat.
// wrapped in useCallback
export const useTodoCount = () => {
return useTodos(useCallback((data) => data.length, []))
}
// wrapped in useCallback
export const useTodoCount = () => {
return useTodos(useCallback((data) => data.length, []))
}
// extracted to a stable function reference
const selectTodoCount = (data) => data.length
export const useTodoCount = () => {
return useTodos(selectTodoCount)
}
// extracted to a stable function reference
const selectTodoCount = (data) => data.length
export const useTodoCount = () => {
return useTodos(selectTodoCount)
}
Für eine ausführliche Anleitung zu diesen Themen lesen Sie React Query Render Optimizations aus den Community-Ressourcen. Um zu erfahren, wie Sie die Option select am besten optimieren, lesen Sie React Query Selectors, Supercharged.