Framework
Version
Integrationen

Externes Laden von Daten

Wichtig

Diese Anleitung konzentriert sich auf externe Zustandsverwaltungsbibliotheken und deren Integration mit TanStack Router für Datenladen, SSR, Hydrierung/Dehydrierung und Streaming. Wenn Sie die Standardanleitung Datenladen noch nicht gelesen haben, tun Sie dies bitte zuerst.

Zum Speichern oder zum Koordinieren?

Während der Router ausgereift genug ist, um die meisten Datenbedürfnisse Out-of-the-Box zu speichern und zu verwalten, möchten Sie manchmal einfach etwas Robusteres haben!

Der Router ist dafür konzipiert, ein perfekter Koordinator für externe Datenlade- und Caching-Bibliotheken zu sein. Das bedeutet, dass Sie jede beliebige Datenlade-/Caching-Bibliothek verwenden können, und der Router wird das Laden Ihrer Daten so koordinieren, dass es mit der Navigation Ihrer Benutzer und deren Erwartungen an Aktualität übereinstimmt.

Welche Datenladebibliotheken werden unterstützt?

Jede Datenladebibliothek, die asynchrone Promises unterstützt, kann mit TanStack Router verwendet werden. Dazu gehören

Oder sogar...

Buchstäblich jede Bibliothek, die einen Promise zurückgeben und Daten lesen/schreiben kann, kann integriert werden.

Verwendung von Loadern zur Sicherstellung, dass Daten geladen werden

Der einfachste Weg, externe Caching-/Datenbibliotheken in den Router zu integrieren, ist die Verwendung von route.loaders, um sicherzustellen, dass die für eine Route benötigten Daten geladen wurden und zur Anzeige bereit sind.

⚠️ ABER WARUM? Es ist sehr wichtig, Ihre kritischen Render-Daten im Loader vorzuladen, aus mehreren Gründen

  • Keine "Flash of loading" Zustände
  • Kein Waterfall-Datenladen, verursacht durch komponentenbasierte Datenladung
  • Besser für SEO. Wenn Ihre Daten zum Zeitpunkt des Renderings verfügbar sind, werden sie von Suchmaschinen indiziert.

Hier ist eine naive Illustration (tun Sie dies nicht) der Verwendung der loader Option einer Route, um den Cache für einige Daten zu füllen.

tsx
// src/routes/posts.tsx
let postsCache = []

export const Route = createFileRoute('/posts')({
  loader: async () => {
    postsCache = await fetchPosts()
  },
  component: () => {
    return (
      <div>
        {postsCache.map((post) => (
          <Post key={post.id} post={post} />
        ))}
      </div>
    )
  },
})
// src/routes/posts.tsx
let postsCache = []

export const Route = createFileRoute('/posts')({
  loader: async () => {
    postsCache = await fetchPosts()
  },
  component: () => {
    return (
      <div>
        {postsCache.map((post) => (
          <Post key={post.id} post={post} />
        ))}
      </div>
    )
  },
})

Dieses Beispiel ist offensichtlich fehlerhaft, veranschaulicht aber den Punkt, dass Sie die loader Option einer Route verwenden können, um Ihren Cache mit Daten zu füllen. Schauen wir uns ein realistischeres Beispiel mit TanStack Query an.

  • Ersetzen Sie fetchPosts durch die Pre-Fetching-API Ihrer bevorzugten Datenladebibliothek
  • Ersetzen Sie postsCache durch die Lese-oder-Abruf-API oder den Hook Ihrer bevorzugten Datenladebibliothek

Ein realistischeres Beispiel mit TanStack Query

Schauen wir uns ein realistischeres Beispiel mit TanStack Query an.

tsx
// src/routes/posts.tsx
const postsQueryOptions = queryOptions({
  queryKey: ['posts'],
  queryFn: () => fetchPosts(),
})

export const Route = createFileRoute('/posts')({
  // Use the `loader` option to ensure that the data is loaded
  loader: () => queryClient.ensureQueryData(postsQueryOptions),
  component: () => {
    // Read the data from the cache and subscribe to updates
    const {
      data: { posts },
    } = useSuspenseQuery(postsQueryOptions)

    return (
      <div>
        {posts.map((post) => (
          <Post key={post.id} post={post} />
        ))}
      </div>
    )
  },
})
// src/routes/posts.tsx
const postsQueryOptions = queryOptions({
  queryKey: ['posts'],
  queryFn: () => fetchPosts(),
})

export const Route = createFileRoute('/posts')({
  // Use the `loader` option to ensure that the data is loaded
  loader: () => queryClient.ensureQueryData(postsQueryOptions),
  component: () => {
    // Read the data from the cache and subscribe to updates
    const {
      data: { posts },
    } = useSuspenseQuery(postsQueryOptions)

    return (
      <div>
        {posts.map((post) => (
          <Post key={post.id} post={post} />
        ))}
      </div>
    )
  },
})

Fehlerbehandlung mit TanStack Query

Wenn ein Fehler auftritt, während suspense mit TanStack Query verwendet wird, müssen Sie die Queries darauf hinweisen, dass Sie versuchen möchten, sie beim erneuten Rendern erneut zu laden. Dies kann durch die Verwendung der reset Funktion geschehen, die vom useQueryErrorResetBoundary Hook bereitgestellt wird. Sie können diese Funktion in einem Effekt aufrufen, sobald die Fehlerkomponente gemountet ist. Dies stellt sicher, dass die Query zurückgesetzt wird und versucht, die Daten erneut abzurufen, wenn die Routenkomponente erneut gerendert wird. Dies deckt auch Fälle ab, in denen Benutzer von der Route weg navigieren, anstatt auf den retry Button zu klicken.

tsx
export const Route = createFileRoute('/')({
  loader: () => queryClient.ensureQueryData(postsQueryOptions),
  errorComponent: ({ error, reset }) => {
    const router = useRouter()
    const queryErrorResetBoundary = useQueryErrorResetBoundary()

    useEffect(() => {
      // Reset the query error boundary
      queryErrorResetBoundary.reset()
    }, [queryErrorResetBoundary])

    return (
      <div>
        {error.message}
        <button
          onClick={() => {
            // Invalidate the route to reload the loader, and reset any router error boundaries
            router.invalidate()
          }}
        >
          retry
        </button>
      </div>
    )
  },
})
export const Route = createFileRoute('/')({
  loader: () => queryClient.ensureQueryData(postsQueryOptions),
  errorComponent: ({ error, reset }) => {
    const router = useRouter()
    const queryErrorResetBoundary = useQueryErrorResetBoundary()

    useEffect(() => {
      // Reset the query error boundary
      queryErrorResetBoundary.reset()
    }, [queryErrorResetBoundary])

    return (
      <div>
        {error.message}
        <button
          onClick={() => {
            // Invalidate the route to reload the loader, and reset any router error boundaries
            router.invalidate()
          }}
        >
          retry
        </button>
      </div>
    )
  },
})

SSR Dehydrierung/Hydrierung

Werkzeuge, die dazu in der Lage sind, können mit den praktischen Dehydrierungs-/Hydrierungs-APIs von TanStack Router integriert werden, um dehydrierte Daten zwischen Server und Client zu übermitteln und sie bei Bedarf neu zu hydrieren. Lassen Sie uns durchgehen, wie dies sowohl mit kritischen 3rd-Party-Daten als auch mit verzögerten 3rd-Party-Daten geschieht.

Kritische Dehydrierung/Hydrierung

Für kritische Daten, die für das erste Rendering/Painting benötigt werden, unterstützt TanStack Router die Optionen dehydrate und hydrate bei der Konfiguration des Router. Diese Callbacks sind Funktionen, die automatisch auf dem Server und Client aufgerufen werden, wenn der Router normal dehydriert und hydriert, und es Ihnen ermöglichen, die dehydrierten Daten mit Ihren eigenen Daten zu erweitern.

Die dehydrate Funktion kann beliebige serialisierbare JSON-Daten zurückgeben, die zusammengeführt und in die an den Client gesendete dehydrierte Nutzlast eingefügt werden.

Lassen Sie uns zum Beispiel einen TanStack Query QueryClient dehydrieren und hydrieren, damit die auf dem Server abgerufenen Daten für die Hydrierung auf dem Client verfügbar sind.

tsx
// src/router.tsx

export function createRouter() {
  // Make sure you create your loader client or similar data
  // stores inside of your `createRouter` function. This ensures
  // that your data stores are unique to each request and
  // always present on both server and client.
  const queryClient = new QueryClient()

  return createRouter({
    routeTree,
    // Optionally provide your loaderClient to the router context for
    // convenience (you can provide anything you want to the router
    // context!)
    context: {
      queryClient,
    },
    // On the server, dehydrate the loader client so the router
    // can serialize it and send it to the client for us
    dehydrate: () => {
      return {
        queryClientState: dehydrate(queryClient),
      }
    },
    // On the client, hydrate the loader client with the data
    // we dehydrated on the server
    hydrate: (dehydrated) => {
      hydrate(queryClient, dehydrated.queryClientState)
    },
    // Optionally, we can use `Wrap` to wrap our router in the loader client provider
    Wrap: ({ children }) => {
      return (
        <QueryClientProvider client={queryClient}>
          {children}
        </QueryClientProvider>
      )
    },
  })
}
// src/router.tsx

export function createRouter() {
  // Make sure you create your loader client or similar data
  // stores inside of your `createRouter` function. This ensures
  // that your data stores are unique to each request and
  // always present on both server and client.
  const queryClient = new QueryClient()

  return createRouter({
    routeTree,
    // Optionally provide your loaderClient to the router context for
    // convenience (you can provide anything you want to the router
    // context!)
    context: {
      queryClient,
    },
    // On the server, dehydrate the loader client so the router
    // can serialize it and send it to the client for us
    dehydrate: () => {
      return {
        queryClientState: dehydrate(queryClient),
      }
    },
    // On the client, hydrate the loader client with the data
    // we dehydrated on the server
    hydrate: (dehydrated) => {
      hydrate(queryClient, dehydrated.queryClientState)
    },
    // Optionally, we can use `Wrap` to wrap our router in the loader client provider
    Wrap: ({ children }) => {
      return (
        <QueryClientProvider client={queryClient}>
          {children}
        </QueryClientProvider>
      )
    },
  })
}
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.