Verzögertes Laden von Daten

TanStack Router ist darauf ausgelegt, Loader parallel auszuführen und zu warten, bis alle aufgelöst sind, bevor die nächste Route gerendert wird. Das ist meistens großartig, aber gelegentlich möchte man dem Benutzer früher etwas anzeigen, während der Rest der Daten im Hintergrund geladen wird.

Deferred Data Loading ist ein Muster, das es dem Router ermöglicht, kritische Daten/Markup der nächsten Location zu rendern, während langsamere, nicht-kritische Routendaten im Hintergrund aufgelöst werden. Dieser Prozess funktioniert sowohl im Client als auch im Server (per Streaming) und ist eine großartige Möglichkeit, die wahrgenommene Leistung Ihrer Anwendung zu verbessern.

Wenn Sie eine Bibliothek wie TanStack Query oder eine andere Datenabrufbibliothek verwenden, funktioniert Deferred Data Loading etwas anders. Springen Sie zum Abschnitt Deferred Data Loading mit externen Bibliotheken für weitere Informationen.

Deferred Data Loading mit Await

Um langsame oder nicht-kritische Daten zu verzögern, geben Sie einen unaufgelösten/nicht erwarteten Promise irgendwo in Ihrer Loader-Antwort zurück.

tsx
// src/routes/posts.$postId.tsx
import { createFileRoute, defer } from '@tanstack/solid-router'

export const Route = createFileRoute('/posts/$postId')({
  loader: async () => {
    // Fetch some slower data, but do not await it
    const slowDataPromise = fetchSlowData()

    // Fetch and await some data that resolves quickly
    const fastData = await fetchFastData()

    return {
      fastData,
      deferredSlowData: slowDataPromise,
    }
  },
})
// src/routes/posts.$postId.tsx
import { createFileRoute, defer } from '@tanstack/solid-router'

export const Route = createFileRoute('/posts/$postId')({
  loader: async () => {
    // Fetch some slower data, but do not await it
    const slowDataPromise = fetchSlowData()

    // Fetch and await some data that resolves quickly
    const fastData = await fetchFastData()

    return {
      fastData,
      deferredSlowData: slowDataPromise,
    }
  },
})

Sobald beliebige erwartete Promises aufgelöst sind, beginnt die nächste Route mit dem Rendering, während die verzögerten Promises weiterhin aufgelöst werden.

In der Komponente können verzögerte Promises aufgelöst und genutzt werden, indem die Await-Komponente verwendet wird.

tsx
// src/routes/posts.$postId.tsx
import { createFileRoute, Await } from '@tanstack/solid-router'

export const Route = createFileRoute('/posts/$postId')({
  // ...
  component: PostIdComponent,
})

function PostIdComponent() {
  const { deferredSlowData, fastData } = Route.useLoaderData()

  // do something with fastData

  return (
    <Await promise={deferredSlowData} fallback={<div>Loading...</div>}>
      {(data) => {
        return <div>{data}</div>
      }}
    </Await>
  )
}
// src/routes/posts.$postId.tsx
import { createFileRoute, Await } from '@tanstack/solid-router'

export const Route = createFileRoute('/posts/$postId')({
  // ...
  component: PostIdComponent,
})

function PostIdComponent() {
  const { deferredSlowData, fastData } = Route.useLoaderData()

  // do something with fastData

  return (
    <Await promise={deferredSlowData} fallback={<div>Loading...</div>}>
      {(data) => {
        return <div>{data}</div>
      }}
    </Await>
  )
}

Tipp

Wenn Ihre Komponente code-split ist, können Sie die getRouteApi-Funktion verwenden, um zu vermeiden, dass Sie die Route-Konfiguration importieren müssen, um Zugriff auf den typisierten useLoaderData()-Hook zu erhalten.

Die Await-Komponente löst den Promise auf, indem sie die nächste Suspense-Grenze auslöst, bis er aufgelöst ist. Danach rendert sie die children der Komponente als Funktion mit den aufgelösten Daten.

Wenn der Promise abgelehnt wird, wirft die Await-Komponente den serialisierten Fehler, der von der nächsten Error-Grenze abgefangen werden kann.

Deferred Data Loading mit externen Bibliotheken

Wenn Ihre Strategie zum Abrufen von Informationen für die Route auf External Data Loading mit einer externen Bibliothek wie TanStack Query basiert, funktioniert Deferred Data Loading etwas anders, da die Bibliothek den Datenabruf und das Caching für Sie außerhalb von TanStack Router übernimmt.

Anstatt also defer und Await zu verwenden, möchten Sie stattdessen den loader der Route verwenden, um den Datenabruf zu starten, und dann die Hooks der Bibliothek verwenden, um auf die Daten in Ihren Komponenten zuzugreifen.

tsx
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { slowDataOptions, fastDataOptions } from '~/api/query-options'

export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ context: { queryClient } }) => {
    // Kick off the fetching of some slower data, but do not await it
    queryClient.prefetchQuery(slowDataOptions())

    // Fetch and await some data that resolves quickly
    await queryClient.ensureQueryData(fastDataOptions())
  },
})
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { slowDataOptions, fastDataOptions } from '~/api/query-options'

export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ context: { queryClient } }) => {
    // Kick off the fetching of some slower data, but do not await it
    queryClient.prefetchQuery(slowDataOptions())

    // Fetch and await some data that resolves quickly
    await queryClient.ensureQueryData(fastDataOptions())
  },
})

Dann können Sie in Ihrer Komponente die Hooks der Bibliothek verwenden, um auf die Daten zuzugreifen.

tsx
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { useSuspenseQuery } from '@tanstack/solid-query'
import { slowDataOptions, fastDataOptions } from '~/api/query-options'

export const Route = createFileRoute('/posts/$postId')({
  // ...
  component: PostIdComponent,
})

function PostIdComponent() {
  const fastData = useSuspenseQuery(fastDataOptions())

  // do something with fastData

  return (
    <Suspense fallback={<div>Loading...</div>}>
      <SlowDataComponent />
    </Suspense>
  )
}

function SlowDataComponent() {
  const data = useSuspenseQuery(slowDataOptions())

  return <div>{data}</div>
}
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { useSuspenseQuery } from '@tanstack/solid-query'
import { slowDataOptions, fastDataOptions } from '~/api/query-options'

export const Route = createFileRoute('/posts/$postId')({
  // ...
  component: PostIdComponent,
})

function PostIdComponent() {
  const fastData = useSuspenseQuery(fastDataOptions())

  // do something with fastData

  return (
    <Suspense fallback={<div>Loading...</div>}>
      <SlowDataComponent />
    </Suspense>
  )
}

function SlowDataComponent() {
  const data = useSuspenseQuery(slowDataOptions())

  return <div>{data}</div>
}

Caching und Invalidierung

Gestreamte Promises folgen demselben Lebenszyklus wie die zugehörigen Loader-Daten. Sie können sogar vorab geladen werden!

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.