Framework
Version
Integrationen

Daten laden

Das Laden von Daten ist eine häufige Anforderung für Webanwendungen und steht im Zusammenhang mit dem Routing. Beim Laden einer Seite für Ihre App ist es ideal, wenn alle asynchronen Anforderungen der Seite so früh wie möglich und parallel abgerufen und erfüllt werden. Der Router ist der beste Ort, um diese asynchronen Abhängigkeiten zu koordinieren, da er normalerweise der einzige Ort in Ihrer App ist, der weiß, wohin sich Benutzer bewegen, bevor Inhalte gerendert werden.

Möglicherweise sind Sie mit getServerSideProps aus Next.js oder loadern aus Remix/React-Router vertraut. TanStack Router bietet ähnliche Funktionen zum Vorabladen/Laden von Assets pro Route parallel, wodurch eine so schnelle Anzeige wie möglich erreicht wird, während Daten abgerufen werden.

Über diese normalen Erwartungen an einen Router hinaus geht TanStack Router noch einen Schritt weiter und bietet **integriertes SWR-Caching**, eine langfristige In-Memory-Caching-Schicht für Routen-Loader. Das bedeutet, dass Sie TanStack Router verwenden können, um Daten für Ihre Routen vorab zu laden, damit sie sofort geladen werden, oder um Routendaten für zuvor besuchte Routen temporär zu cachen, um sie später wiederzuverwenden.

Der Lifecycle des Routen-Ladens

Jedes Mal, wenn eine URL/Historienaktualisierung erkannt wird, führt der Router die folgende Sequenz aus

  • Routenabgleich (von oben nach unten)
    • route.params.parse
    • route.validateSearch
  • Routen-Vorabladung (Seriell)
    • route.beforeLoad
    • route.onError
      • route.errorComponent / parentRoute.errorComponent / router.defaultErrorComponent
  • Routenladung (parallel)
    • route.component.preload?
    • route.loader
      • route.pendingComponent (Optional)
      • route.component
    • route.onError
      • route.errorComponent / parentRoute.errorComponent / router.defaultErrorComponent

Zum Router-Cache oder nicht zum Router-Cache?

Es ist sehr wahrscheinlich, dass der Router-Cache von TanStack gut für die meisten kleineren bis mittleren Anwendungen geeignet ist, aber es ist wichtig, die Kompromisse zu verstehen, wenn man ihn gegenüber einer robusteren Caching-Lösung wie TanStack Query verwendet.

Vorteile des TanStack Router-Caches

  • Integriert, einfach zu bedienen, keine zusätzlichen Abhängigkeiten
  • Behandelt Deduping, Vorabladung, Laden, Stale-While-Revalidate, Hintergrund-Refetching auf Pro-Route-Basis
  • Grobe Invalidierung (alle Routen und den Cache auf einmal invalidieren)
  • Automatische Garbage Collection
  • Funktioniert gut für Apps, die wenig Daten zwischen Routen austauschen
  • "Funktioniert einfach" für SSR

Nachteile des TanStack Router-Caches

  • Keine Persistenzadapter/-modelle
  • Kein geteiltes Caching/Deduping zwischen Routen
  • Keine integrierten Mutations-APIs (ein einfacher useMutation-Hook ist in vielen Beispielen enthalten, was für viele Anwendungsfälle ausreichend sein kann)
  • Keine integrierten Cache-Level-optimistischen Update-APIs (Sie können immer noch ephemere Zustände von etwas wie einem useMutation-Hook verwenden, um dies auf Komponentenebene zu erreichen)

Tipp

Wenn Sie sofort wissen, dass Sie etwas Robusteres wie TanStack Query verwenden möchten oder müssen, springen Sie zum Leitfaden External Data Loading.

Verwendung des Router-Caches

Der Router-Cache ist integriert und so einfach, dass Sie Daten aus der loader-Funktion jeder Route zurückgeben. Lassen Sie uns lernen, wie!

Routen- loaders

Routen- loader-Funktionen werden aufgerufen, wenn ein Routen-Match geladen wird. Sie werden mit einem einzigen Parameter aufgerufen, einem Objekt, das viele hilfreiche Eigenschaften enthält. Wir werden diese kurz behandeln, aber zuerst schauen wir uns ein Beispiel für eine Routen- loader-Funktion an.

tsx
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
})
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
})

loader-Parameter

Die loader-Funktion empfängt ein einzelnes Objekt mit den folgenden Eigenschaften

  • abortController - Der abortController der Route. Sein Signal wird abgebrochen, wenn die Route entladen wird oder wenn die Route nicht mehr relevant ist und die aktuelle Ausführung der loader-Funktion veraltet ist.
  • cause - Die Ursache des aktuellen Routen-Matches. Kann eine der folgenden sein
    • enter - Wenn die Route übereinstimmt und geladen wird, nachdem sie am vorherigen Ort nicht übereingestimmt hat.
    • preload - Wenn die Route vorab geladen wird.
    • stay - Wenn die Route übereinstimmt und geladen wird, nachdem sie am vorherigen Ort übereingestimmt hat.
  • context - Das Kontextobjekt der Route, das eine zusammengeführte Vereinigung von Folgendem ist
    • Kontext der übergeordneten Route
    • Der Kontext dieser Route, wie er durch die beforeLoad-Option bereitgestellt wird
  • deps - Der von der Route.loaderDeps-Funktion zurückgegebene Objektwert. Wenn Route.loaderDeps nicht definiert ist, wird stattdessen ein leeres Objekt bereitgestellt.
  • location - Der aktuelle Ort
  • params - Die Pfadparameter der Route
  • parentMatchPromise - Promise<RouteMatch> (undefined für die Root-Route)
  • preload - Boolescher Wert, der true ist, wenn die Route anstelle des Ladens vorab geladen wird
  • route - Die Route selbst

Mit diesen Parametern können wir viele coole Dinge tun, aber schauen wir uns zuerst an, wie wir sie steuern können und wann die loader-Funktion aufgerufen wird.

Daten aus loaders konsumieren

Um Daten aus einem loader zu konsumieren, verwenden Sie den Hook useLoaderData, der auf Ihrem Route-Objekt definiert ist.

tsx
const posts = Route.useLoaderData()
const posts = Route.useLoaderData()

Wenn Sie keinen direkten Zugriff auf Ihr Routenobjekt haben (d. h. Sie befinden sich tief im Komponentendbaum der aktuellen Route), können Sie getRouteApi verwenden, um auf denselben Hook (sowie die anderen Hooks des Route-Objekts) zuzugreifen. Dies sollte dem Importieren des Route-Objekts vorgezogen werden, was wahrscheinlich zu zirkulären Abhängigkeiten führen würde.

tsx
import { getRouteApi } from '@tanstack/react-router'

// in your component

const routeApi = getRouteApi('/posts')
const data = routeApi.useLoaderData()
import { getRouteApi } from '@tanstack/react-router'

// in your component

const routeApi = getRouteApi('/posts')
const data = routeApi.useLoaderData()

Abhängigkeitsbasierte Stale-While-Revalidate-Caching

TanStack Router bietet eine integrierte Stale-While-Revalidate-Caching-Schicht für Routen-Loader, die auf den Abhängigkeiten einer Route basiert

  • Der vollständig analysierte Pfadname der Route
    • z. B. /posts/1 vs /posts/2
  • Alle zusätzlichen Abhängigkeiten, die von der loaderDeps-Option bereitgestellt werden
    • z. B. loaderDeps: ({ search: { pageIndex, pageSize } }) => ({ pageIndex, pageSize })

Mit diesen Abhängigkeiten als Schlüssel speichert TanStack Router die von der loader-Funktion einer Route zurückgegebenen Daten und verwendet sie zur Erfüllung nachfolgender Anfragen für denselben Routen-Match. Das bedeutet, wenn die Daten einer Route bereits im Cache vorhanden sind, werden sie sofort zurückgegeben, dann **möglicherweise** im Hintergrund erneut abgerufen, abhängig von der "Aktualität" der Daten.

Schlüsseloptionen

Um Routenabhängigkeiten und "Aktualität" zu steuern, bietet TanStack Router eine Fülle von Optionen zur Steuerung des Schlüssel- und Caching-Verhaltens Ihrer Routen-Loader. Schauen wir uns diese in der Reihenfolge an, in der Sie sie am wahrscheinlichsten verwenden werden.

  • routeOptions.loaderDeps
    • Eine Funktion, die Ihnen die Suchparameter für einen Router liefert und ein Objekt von Abhängigkeiten zurückgibt, die in Ihrer loader-Funktion verwendet werden. Wenn sich diese Abhängigkeiten von einer Navigation zur nächsten ändern, wird die Route neu geladen, unabhängig von staleTimes. Die Abhängigkeiten werden anhand einer tiefen Gleichheitsprüfung verglichen.
  • routeOptions.staleTime
  • routerOptions.defaultStaleTime
    • Die Anzahl der Millisekunden, in denen die Daten einer Route beim Laden als frisch gelten sollen.
  • routeOptions.preloadStaleTime
  • routerOptions.defaultPreloadStaleTime
    • Die Anzahl der Millisekunden, in denen die Daten einer Route beim Vorabladen als frisch gelten sollen.
  • routeOptions.gcTime
  • routerOptions.defaultGcTime
    • Die Anzahl der Millisekunden, in denen die Daten einer Route im Cache gehalten werden, bevor sie gesammelt werden.
  • routeOptions.shouldReload
    • Eine Funktion, die dieselben beforeLoad- und loaderContext-Parameter empfängt und einen booleschen Wert zurückgibt, der angibt, ob die Route neu geladen werden soll. Dies bietet eine weitere Ebene der Kontrolle darüber, wann eine Route neu geladen werden soll, über staleTime und loaderDeps hinaus und kann zur Implementierung von Mustern ähnlich der shouldLoad-Option von Remix verwendet werden.

⚠️ Einige wichtige Standardeinstellungen

  • Standardmäßig ist die staleTime auf 0 gesetzt, was bedeutet, dass die Daten der Route immer als veraltet gelten und immer im Hintergrund neu geladen werden, wenn die Route erneut abgeglichen wird.
  • Standardmäßig gelten vorab geladene Routendaten für **30 Sekunden** als frisch. Das bedeutet, wenn eine Route geladen und dann innerhalb von 30 Sekunden erneut vorab geladen wird, wird die zweite Vorabladung ignoriert. Dies verhindert, dass unnötige Vorabladungen zu häufig erfolgen. **Wenn eine Route normal geladen wird, wird die Standard- staleTime verwendet.**
  • Standardmäßig ist die gcTime auf **30 Minuten** gesetzt, was bedeutet, dass alle Routendaten, auf die 30 Minuten lang nicht zugegriffen wurde, gesammelt und aus dem Cache entfernt werden.
  • router.invalidate() erzwingt, dass alle aktiven Routen ihre Loader sofort neu laden und markiert die Daten jeder gecachten Route als veraltet.

Verwendung von loaderDeps zum Zugriff auf Suchparameter

Stellen Sie sich vor, eine /posts-Route unterstützt eine Paginierung über die Suchparameter offset und limit. Damit der Cache diese Daten eindeutig speichern kann, müssen wir über die loaderDeps-Funktion auf diese Suchparameter zugreifen. Indem wir sie explizit identifizieren, werden die einzelnen Routen-Matches für /posts mit unterschiedlichen offset und limit nicht vermischt!

Sobald wir diese Abhängigkeiten eingerichtet haben, wird die Route bei Änderungen der Abhängigkeiten immer neu geladen.

tsx
// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loaderDeps: ({ search: { offset, limit } }) => ({ offset, limit }),
  loader: ({ deps: { offset, limit } }) =>
    fetchPosts({
      offset,
      limit,
    }),
})
// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loaderDeps: ({ search: { offset, limit } }) => ({ offset, limit }),
  loader: ({ deps: { offset, limit } }) =>
    fetchPosts({
      offset,
      limit,
    }),
})

Verwendung von staleTime zur Steuerung, wie lange Daten als frisch gelten

Standardmäßig ist die staleTime für Navigationen auf 0 ms (und 30 Sekunden für Vorabladungen) gesetzt, was bedeutet, dass die Daten der Route immer als veraltet gelten und immer im Hintergrund neu geladen werden, wenn die Route übereinstimmt und zu ihr navigiert wird.

Dies ist eine gute Standardeinstellung für die meisten Anwendungsfälle, aber Sie werden feststellen, dass einige Routendaten statischer oder potenziell teuer im Laden sind. In diesen Fällen können Sie die Option staleTime verwenden, um zu steuern, wie lange die Daten der Route für Navigationen als frisch gelten. Schauen wir uns ein Beispiel an.

tsx
// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  // Consider the route's data fresh for 10 seconds
  staleTime: 10_000,
})
// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  // Consider the route's data fresh for 10 seconds
  staleTime: 10_000,
})

Durch die Übergabe von 10_000 an die Option staleTime teilen wir dem Router mit, die Daten der Route für 10 Sekunden als frisch zu betrachten. Das bedeutet, wenn der Benutzer innerhalb von 10 Sekunden nach dem letzten Loader-Ergebnis von /about zu /posts navigiert, werden die Daten der Route nicht neu geladen. Wenn der Benutzer dann nach 10 Sekunden von /about zu /posts navigiert, werden die Daten der Route **im Hintergrund** neu geladen.

Deaktivieren des Stale-While-Revalidate-Cachings

Um das Stale-While-Revalidate-Caching für eine Route zu deaktivieren, setzen Sie die Option staleTime auf Infinity.

tsx
// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  staleTime: Infinity,
})
// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  staleTime: Infinity,
})

Sie können dies sogar für alle Routen deaktivieren, indem Sie die Option defaultStaleTime auf dem Router setzen.

tsx
const router = createRouter({
  routeTree,
  defaultStaleTime: Infinity,
})
const router = createRouter({
  routeTree,
  defaultStaleTime: Infinity,
})

Verwendung von shouldReload und gcTime zur Deaktivierung des Cachings

Ähnlich wie bei der Standardfunktionalität von Remix möchten Sie möglicherweise eine Route so konfigurieren, dass sie nur beim Eintritt oder bei Änderung kritischer Loader-Abhängigkeiten geladen wird. Dies können Sie mit der Option gcTime in Kombination mit der Option shouldReload erreichen, die entweder einen boolean oder eine Funktion akzeptiert, die dieselben beforeLoad- und loaderContext-Parameter empfängt und einen booleschen Wert zurückgibt, der angibt, ob die Route neu geladen werden soll.

tsx
// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loaderDeps: ({ search: { offset, limit } }) => ({ offset, limit }),
  loader: ({ deps }) => fetchPosts(deps),
  // Do not cache this route's data after it's unloaded
  gcTime: 0,
  // Only reload the route when the user navigates to it or when deps change
  shouldReload: false,
})
// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loaderDeps: ({ search: { offset, limit } }) => ({ offset, limit }),
  loader: ({ deps }) => fetchPosts(deps),
  // Do not cache this route's data after it's unloaded
  gcTime: 0,
  // Only reload the route when the user navigates to it or when deps change
  shouldReload: false,
})

Deaktivieren des Cachings bei gleichzeitiger Vorabladung

Auch wenn Sie kurzfristiges Caching für Ihre Routendaten deaktivieren, können Sie dennoch von der Vorabladung profitieren! Mit der obigen Konfiguration funktioniert die Vorabladung weiterhin "einfach" mit der Standard- preloadGcTime. Das bedeutet, wenn eine Route vorab geladen und dann navigiert wird, gelten die Daten der Route als frisch und werden nicht neu geladen.

Um die Vorabladung zu deaktivieren, schalten Sie sie nicht über die Optionen routerOptions.defaultPreload oder routeOptions.preload ein.

Übergabe aller Loader-Events an einen externen Cache

Wir behandeln diesen Anwendungsfall auf der Seite External Data Loading, aber wenn Sie einen externen Cache wie TanStack Query verwenden möchten, können Sie dies tun, indem Sie alle Loader-Events an Ihren externen Cache übergeben. Solange Sie die Standardeinstellungen verwenden, besteht die einzige Änderung, die Sie vornehmen müssen, darin, die Option defaultPreloadStaleTime für den Router auf 0 zu setzen.

tsx
const router = createRouter({
  routeTree,
  defaultPreloadStaleTime: 0,
})
const router = createRouter({
  routeTree,
  defaultPreloadStaleTime: 0,
})

Dies stellt sicher, dass jedes Vorablade-, Lade- und Neu-Lade-Ereignis Ihre loader-Funktionen auslöst, die dann von Ihrem externen Cache verarbeitet und dedupliziert werden können.

Verwendung des Router-Kontexts

Das context-Argument, das an die loader-Funktion übergeben wird, ist ein Objekt, das eine zusammengeführte Vereinigung von Folgendem enthält.

  • Kontext der übergeordneten Route
  • Der Kontext dieser Route, wie er durch die beforeLoad-Option bereitgestellt wird

Ganz oben im Router können Sie einen anfänglichen Kontext an den Router über die Option context übergeben. Dieser Kontext ist für alle Routen im Router verfügbar und wird von jeder Route kopiert und erweitert, wenn sie übereinstimmt. Dies geschieht durch Übergabe eines Kontexts an eine Route über die Option beforeLoad. Dieser Kontext ist für alle untergeordneten Routen der Route verfügbar. Der resultierende Kontext ist für die loader-Funktion der Route verfügbar.

In diesem Beispiel erstellen wir eine Funktion in unserem Routenkontext zum Abrufen von Beiträgen und verwenden sie dann in unserer loader-Funktion.

🧠 Kontext ist ein mächtiges Werkzeug für Dependency Injection. Sie können ihn verwenden, um Dienste, Hooks und andere Objekte in Ihren Router und Ihre Routen zu injizieren. Sie können auch Daten mit der Option beforeLoad jeder Route additiv nach unten durch den Routenbaum übergeben.

  • /utils/fetchPosts.tsx
tsx
export const fetchPosts = async () => {
  const res = await fetch(`/api/posts?page=${pageIndex}`)
  if (!res.ok) throw new Error('Failed to fetch posts')
  return res.json()
}
export const fetchPosts = async () => {
  const res = await fetch(`/api/posts?page=${pageIndex}`)
  if (!res.ok) throw new Error('Failed to fetch posts')
  return res.json()
}
  • /routes/__root.tsx
tsx
import { createRootRouteWithContext } from '@tanstack/react-router'

// Create a root route using the createRootRouteWithContext<{...}>() function and pass it whatever types you would like to be available in your router context.
export const Route = createRootRouteWithContext<{
  fetchPosts: typeof fetchPosts
}>()() // NOTE: the double call is on purpose, since createRootRouteWithContext is a factory ;)
import { createRootRouteWithContext } from '@tanstack/react-router'

// Create a root route using the createRootRouteWithContext<{...}>() function and pass it whatever types you would like to be available in your router context.
export const Route = createRootRouteWithContext<{
  fetchPosts: typeof fetchPosts
}>()() // NOTE: the double call is on purpose, since createRootRouteWithContext is a factory ;)
  • /routes/posts.tsx
tsx
import { createFileRoute } from '@tanstack/react-router'

// Notice how our postsRoute references context to get our fetchPosts function
// This can be a powerful tool for dependency injection across your router
// and routes.
export const Route = createFileRoute('/posts')({
  loader: ({ context: { fetchPosts } }) => fetchPosts(),
})
import { createFileRoute } from '@tanstack/react-router'

// Notice how our postsRoute references context to get our fetchPosts function
// This can be a powerful tool for dependency injection across your router
// and routes.
export const Route = createFileRoute('/posts')({
  loader: ({ context: { fetchPosts } }) => fetchPosts(),
})
  • /router.tsx
tsx
import { routeTree } from './routeTree.gen'

// Use your routerContext to create a new router
// This will require that you fullfil the type requirements of the routerContext
const router = createRouter({
  routeTree,
  context: {
    // Supply the fetchPosts function to the router context
    fetchPosts,
  },
})
import { routeTree } from './routeTree.gen'

// Use your routerContext to create a new router
// This will require that you fullfil the type requirements of the routerContext
const router = createRouter({
  routeTree,
  context: {
    // Supply the fetchPosts function to the router context
    fetchPosts,
  },
})

Verwendung von Pfadparametern

Um Pfadparameter in Ihrer loader-Funktion zu verwenden, greifen Sie über die Eigenschaft params auf die Parameter der Funktion zu. Hier ist ein Beispiel.

tsx
// routes/posts.$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
  loader: ({ params: { postId } }) => fetchPostById(postId),
})
// routes/posts.$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
  loader: ({ params: { postId } }) => fetchPostById(postId),
})

Verwendung von Routen-Kontext

Das Übergeben eines globalen Kontexts an Ihren Router ist großartig, aber was ist, wenn Sie einen Kontext bereitstellen möchten, der spezifisch für eine Route ist? Hier kommt die Option beforeLoad ins Spiel. Die Option beforeLoad ist eine Funktion, die kurz vor dem Versuch, eine Route zu laden, ausgeführt wird und dieselben Parameter wie loader empfängt. Neben ihrer Fähigkeit, potenzielle Übereinstimmungen umzuleiten, Loader-Anfragen zu blockieren usw., kann sie auch ein Objekt zurückgeben, das in den Kontext der Route zusammengeführt wird. Schauen wir uns ein Beispiel an, in dem wir einige Daten über die Option beforeLoad in unseren Routenkontext einfügen.

tsx
// /routes/posts.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
  // Pass the fetchPosts function to the route context
  beforeLoad: () => ({
    fetchPosts: () => console.info('foo'),
  }),
  loader: ({ context: { fetchPosts } }) => {
    console.info(fetchPosts()) // 'foo'

    // ...
  },
})
// /routes/posts.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
  // Pass the fetchPosts function to the route context
  beforeLoad: () => ({
    fetchPosts: () => console.info('foo'),
  }),
  loader: ({ context: { fetchPosts } }) => {
    console.info(fetchPosts()) // 'foo'

    // ...
  },
})

Verwendung von Suchparametern in Loadern

❓ Aber Moment mal, Tanner... wo zum Teufel sind meine Suchparameter?!

Sie fragen sich vielleicht, warum search nicht direkt in den Parametern der loader-Funktion verfügbar ist. Wir haben es absichtlich so gestaltet, um Ihnen zum Erfolg zu verhelfen. Lassen Sie uns sehen, warum.

  • Suchparameter, die in einer Loader-Funktion verwendet werden, sind ein sehr guter Indikator dafür, dass diese Suchparameter auch zur eindeutigen Identifizierung der geladenen Daten verwendet werden sollten. Zum Beispiel könnten Sie eine Route haben, die einen Suchparameter wie pageIndex verwendet, der die in dem Routen-Match enthaltenen Daten eindeutig identifiziert. Oder stellen Sie sich eine /users/user-Route vor, die den Suchparameter userId verwendet, um einen bestimmten Benutzer in Ihrer Anwendung zu identifizieren. Sie könnten Ihre URL wie folgt modellieren: /users/user?userId=123. Das bedeutet, dass Ihre user-Route zusätzliche Hilfe benötigt, um einen bestimmten Benutzer zu identifizieren.
  • Der direkte Zugriff auf Suchparameter in einer Loader-Funktion kann zu Fehlern beim Caching und bei der Vorabladung führen, bei denen die geladenen Daten nicht eindeutig für den aktuellen URL-Pfad und die Suchparameter sind. Sie könnten beispielsweise Ihre /posts-Route bitten, die Ergebnisse von Seite 2 vorab zu laden, aber ohne die Unterscheidung der Seiten in Ihrer Routenkonfiguration würden Sie am Ende Seiten-2-Daten auf Ihrem /posts oder ?page=1-Bildschirm abrufen, speichern und anzeigen, anstatt sie im Hintergrund vorab zu laden!
  • Das Platzieren eines Schwellenwerts zwischen Suchparametern und der Loader-Funktion ermöglicht es dem Router, Ihre Abhängigkeiten und Reaktivität zu verstehen.
tsx
// /routes/users.user.tsx
export const Route = createFileRoute('/users/user')({
  validateSearch: (search) =>
    search as {
      userId: string
    },
  loaderDeps: ({ search: { userId } }) => ({
    userId,
  }),
  loader: async ({ deps: { userId } }) => getUser(userId),
})
// /routes/users.user.tsx
export const Route = createFileRoute('/users/user')({
  validateSearch: (search) =>
    search as {
      userId: string
    },
  loaderDeps: ({ search: { userId } }) => ({
    userId,
  }),
  loader: async ({ deps: { userId } }) => getUser(userId),
})

Zugriff auf Suchparameter über routeOptions.loaderDeps

tsx
// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
  // Use zod to validate and parse the search params
  validateSearch: z.object({
    offset: z.number().int().nonnegative().catch(0),
  }),
  // Pass the offset to your loader deps via the loaderDeps function
  loaderDeps: ({ search: { offset } }) => ({ offset }),
  // Use the offset from context in the loader function
  loader: async ({ deps: { offset } }) =>
    fetchPosts({
      offset,
    }),
})
// /routes/posts.tsx
export const Route = createFileRoute('/posts')({
  // Use zod to validate and parse the search params
  validateSearch: z.object({
    offset: z.number().int().nonnegative().catch(0),
  }),
  // Pass the offset to your loader deps via the loaderDeps function
  loaderDeps: ({ search: { offset } }) => ({ offset }),
  // Use the offset from context in the loader function
  loader: async ({ deps: { offset } }) =>
    fetchPosts({
      offset,
    }),
})

Verwendung des Abort-Signals

Die Eigenschaft abortController der loader-Funktion ist ein AbortController. Sein Signal wird abgebrochen, wenn die Route entladen wird oder wenn der loader-Aufruf veraltet ist. Dies ist nützlich, um Netzwerkanfragen abzubrechen, wenn die Route entladen wird oder wenn sich die Parameter der Route ändern. Hier ist ein Beispiel, das es mit einem Fetch-Aufruf verwendet.

tsx
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: ({ abortController }) =>
    fetchPosts({
      // Pass this to an underlying fetch call or anything that supports signals
      signal: abortController.signal,
    }),
})
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: ({ abortController }) =>
    fetchPosts({
      // Pass this to an underlying fetch call or anything that supports signals
      signal: abortController.signal,
    }),
})

Verwendung des preload-Flags

Die Eigenschaft preload der loader-Funktion ist ein boolescher Wert, der true ist, wenn die Route anstelle des Ladens vorab geladen wird. Einige Datenladebibliotheken verarbeiten die Vorabladung möglicherweise anders als ein Standard-Fetch, sodass Sie preload an Ihre Datenladebibliothek übergeben oder sie verwenden, um die entsprechende Datenlade-Logik auszuführen.

tsx
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: async ({ preload }) =>
    fetchPosts({
      maxAge: preload ? 10_000 : 0, // Preloads should hang around a bit longer
    }),
})
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: async ({ preload }) =>
    fetchPosts({
      maxAge: preload ? 10_000 : 0, // Preloads should hang around a bit longer
    }),
})

Umgang mit langsamen Loadern

Idealerweise können die meisten Routen-Loader ihre Daten innerhalb eines kurzen Moments auflösen, wodurch die Notwendigkeit entfällt, einen Platzhalter-Spinner anzuzeigen, und sich einfach auf Suspense verlassen, um die nächste Route zu rendern, wenn sie vollständig bereit ist. Wenn jedoch kritische Daten, die zum Rendern der Komponente einer Route erforderlich sind, langsam sind, haben Sie 2 Optionen

  • Teilen Sie Ihre schnellen und langsamen Daten in separate Promises auf und deferen Sie die langsamen Daten, bis die schnellen Daten geladen sind (siehe Leitfaden Deferred Data Loading).
  • Zeigen Sie eine ausstehende Komponente nach einem optimistischen Suspense-Schwellenwert an, bis alle Daten bereit sind (siehe unten).

Anzeigen einer ausstehenden Komponente

Standardmäßig zeigt TanStack Router eine ausstehende Komponente für Loader an, die länger als 1 Sekunde zum Auflösen benötigen. Dies ist ein optimistischer Schwellenwert, der konfiguriert werden kann über

  • routeOptions.pendingMs oder
  • routerOptions.defaultPendingMs

Wenn der Schwellenwert für die Wartezeit überschritten wird, rendert der Router die Option pendingComponent der Route, falls konfiguriert.

Vermeidung von "Flash" der ausstehenden Komponente

Wenn Sie eine ausstehende Komponente verwenden, ist das Letzte, was Sie wollen, dass Ihr Wartezeit-Schwellenwert erreicht wird und Ihre Daten dann sofort danach aufgelöst werden, was zu einem störenden Aufleuchten Ihrer ausstehenden Komponente führt. Um dies zu vermeiden, **zeigt TanStack Router standardmäßig Ihre ausstehende Komponente für mindestens 500 ms an**. Dies ist ein optimistischer Schwellenwert, der konfiguriert werden kann über

  • routeOptions.pendingMinMs oder
  • routerOptions.defaultPendingMinMs

Fehlerbehandlung

TanStack Router bietet verschiedene Möglichkeiten, Fehler zu behandeln, die während des Routen-Lade-Lifecycles auftreten. Schauen wir sie uns an.

Fehlerbehandlung mit routeOptions.onError

Die Option routeOptions.onError ist eine Funktion, die aufgerufen wird, wenn während des Routen-Ladens ein Fehler auftritt.

tsx
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  onError: ({ error }) => {
    // Log the error
    console.error(error)
  },
})
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  onError: ({ error }) => {
    // Log the error
    console.error(error)
  },
})

Fehlerbehandlung mit routeOptions.onCatch

Die Option routeOptions.onCatch ist eine Funktion, die aufgerufen wird, wenn ein Fehler vom CatchBoundary des Routers abgefangen wurde.

tsx
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  onCatch: ({ error, errorInfo }) => {
    // Log the error
    console.error(error)
  },
})
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  onCatch: ({ error, errorInfo }) => {
    // Log the error
    console.error(error)
  },
})

Fehlerbehandlung mit routeOptions.errorComponent

Die Option routeOptions.errorComponent ist eine Komponente, die gerendert wird, wenn während des Routen-Lade- oder Rendering-Lifecycles ein Fehler auftritt. Sie wird mit den folgenden Props gerendert

  • error - Der aufgetretene Fehler
  • reset - Eine Funktion zum Zurücksetzen des internen CatchBoundary
tsx
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  errorComponent: ({ error }) => {
    // Render an error message
    return <div>{error.message}</div>
  },
})
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  errorComponent: ({ error }) => {
    // Render an error message
    return <div>{error.message}</div>
  },
})

Die Funktion reset kann verwendet werden, um dem Benutzer zu ermöglichen, das erneute Rendern der normalen Kinder der Fehlergrenze zu versuchen.

tsx
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  errorComponent: ({ error, reset }) => {
    return (
      <div>
        {error.message}
        <button
          onClick={() => {
            // Reset the router error boundary
            reset()
          }}
        >
          retry
        </button>
      </div>
    )
  },
})
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  errorComponent: ({ error, reset }) => {
    return (
      <div>
        {error.message}
        <button
          onClick={() => {
            // Reset the router error boundary
            reset()
          }}
        >
          retry
        </button>
      </div>
    )
  },
})

Wenn der Fehler das Ergebnis eines Routen-Ladevorgangs war, sollten Sie stattdessen router.invalidate() aufrufen, was sowohl einen Router-Neuladevorgang als auch ein Zurücksetzen der Fehlergrenze koordiniert.

tsx
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  errorComponent: ({ error, reset }) => {
    const router = useRouter()

    return (
      <div>
        {error.message}
        <button
          onClick={() => {
            // Invalidate the route to reload the loader, which will also reset the error boundary
            router.invalidate()
          }}
        >
          retry
        </button>
      </div>
    )
  },
})
// routes/posts.tsx
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  errorComponent: ({ error, reset }) => {
    const router = useRouter()

    return (
      <div>
        {error.message}
        <button
          onClick={() => {
            // Invalidate the route to reload the loader, which will also reset the error boundary
            router.invalidate()
          }}
        >
          retry
        </button>
      </div>
    )
  },
})

Verwendung der Standard- ErrorComponent

TanStack Router stellt eine Standard- ErrorComponent bereit, die gerendert wird, wenn während des Routen-Lade- oder Rendering-Lifecycles ein Fehler auftritt. Wenn Sie die Fehlerkomponenten Ihrer Routen überschreiben, ist es immer noch ratsam, alle nicht abgefangenen Fehler mit der Standard- ErrorComponent aufzufangen.

tsx
// routes/posts.tsx
import { createFileRoute, ErrorComponent } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  errorComponent: ({ error }) => {
    if (error instanceof MyCustomError) {
      // Render a custom error message
      return <div>{error.message}</div>
    }

    // Fallback to the default ErrorComponent
    return <ErrorComponent error={error} />
  },
})
// routes/posts.tsx
import { createFileRoute, ErrorComponent } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
  errorComponent: ({ error }) => {
    if (error instanceof MyCustomError) {
      // Render a custom error message
      return <div>{error.message}</div>
    }

    // Fallback to the default ErrorComponent
    return <ErrorComponent error={error} />
  },
})
Unsere Partner
Code Rabbit
Netlify
Neon
Clerk
Convex
Sentry
Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.

Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.