Framework
Version
Integrationen

Suchparameter (Search Params)

Ähnlich wie TanStack Query die Handhabung von Serverzuständen in Ihren React- und Solid-Anwendungen vereinfacht hat, zielt TanStack Router darauf ab, die Leistung von URL-Suchparametern in Ihren Anwendungen freizusetzen.

🧠 Wenn Sie einen sehr alten Browser wie IE11 verwenden, müssen Sie möglicherweise einen Polyfill für URLSearchParams verwenden.

Warum nicht einfach URLSearchParams verwenden?

Wir verstehen es, Sie haben in letzter Zeit viel "nutzen Sie die Plattform" gehört, und meistens stimmen wir zu. Wir glauben jedoch auch, dass es wichtig ist, zu erkennen, wo die Plattform für fortgeschrittenere Anwendungsfälle hinter den Erwartungen zurückbleibt, und wir glauben, dass URLSearchParams einer dieser Fälle ist.

Traditionelle Suchparameter-APIs gehen normalerweise von einigen Annahmen aus

  • Suchparameter sind immer Zeichenketten
  • Sie sind *meistens* flach
  • Die Serialisierung und Deserialisierung mit URLSearchParams ist gut genug (Spoiler-Alarm: ist sie nicht.)
  • Änderungen an Suchparametern sind eng mit dem Pfadnamen der URL gekoppelt und müssen zusammen aktualisiert werden, auch wenn sich der Pfadname nicht ändert.

Die Realität unterscheidet sich jedoch stark von diesen Annahmen.

  • Suchparameter stellen den Anwendungszustand dar, daher erwarten wir zwangsläufig, dass sie die gleiche DX aufweisen wie andere Zustandsmanager. Dies bedeutet, dass die Fähigkeit besteht, zwischen primitiven Werttypen zu unterscheiden und komplexe Datenstrukturen wie verschachtelte Arrays und Objekte effizient zu speichern und zu manipulieren.
  • Es gibt viele Möglichkeiten, Zustände mit unterschiedlichen Kompromissen zu serialisieren und zu deserialisieren. Sie sollten in der Lage sein, die beste für Ihre Anwendung auszuwählen oder zumindest einen besseren Standard als URLSearchParams zu erhalten.
  • Unveränderlichkeit & strukturelles Teilen. Jedes Mal, wenn Sie URL-Suchparameter serialisieren und parsen, gehen Referenzintegrität und Objektidentität verloren, da jede neue Analyse eine brandneue Datenstruktur mit einer eindeutigen Speicherreferenz erstellt. Wenn dies über seine Lebensdauer nicht ordnungsgemäß verwaltet wird, kann diese ständige Serialisierung und Analyse zu unerwarteten und unerwünschten Leistungsproblemen führen, insbesondere in Frameworks wie React, die Reaktivität über Unveränderlichkeit verfolgen, oder in Solid, das normalerweise auf Abgleich zur Erkennung von Änderungen aus deserialisierten Datenquellen angewiesen ist.
  • Suchparameter sind zwar ein wichtiger Teil der URL, ändern sich aber häufig unabhängig vom Pfadnamen der URL. Zum Beispiel möchte ein Benutzer die Seitenzahl einer paginierten Liste ändern, ohne den Pfadnamen der URL zu berühren.

Suchparameter, der "OG" State Manager

Sie haben wahrscheinlich Suchparameter wie ?page=3 oder ?filter-name=tanner in der URL gesehen. Es besteht kein Zweifel, dass dies tatsächlich **eine Form des globalen Zustands** ist, der sich in der URL befindet. Es ist wertvoll, bestimmte Zustände in der URL zu speichern, weil

  • Benutzer sollten in der Lage sein
    • Cmd/Ctrl + Klick, um einen Link in einem neuen Tab zu öffnen und zuverlässig den erwarteten Zustand zu sehen
    • Lesezeichen setzen und Links aus Ihrer Anwendung mit anderen teilen, mit der Zusicherung, dass sie genau den Zustand sehen, der beim Kopieren des Links vorhanden war.
    • Ihre App aktualisieren oder zwischen Seiten navigieren, ohne ihren Zustand zu verlieren
  • Entwickler sollten einfach in der Lage sein
    • Zustand in der URL mit der gleichen großartigen DX wie andere Zustandsmanager hinzufügen, entfernen oder ändern
    • Suchparameter, die von der URL kommen, einfach validieren in einem Format und Typ, das für ihre Anwendung sicher zu verwenden ist
    • Suchparameter lesen und schreiben, ohne sich um das zugrunde liegende Serialisierungsformat kümmern zu müssen

JSON-First Suchparameter

Um das oben Genannte zu erreichen, ist der erste Schritt in TanStack Router ein leistungsstarker Suchparameter-Parser, der die Suchzeichenkette Ihrer URL automatisch in strukturiertes JSON umwandelt. Das bedeutet, dass Sie jede JSON-serialisierbare Datenstruktur in Ihren Suchparametern speichern können und sie wird als JSON geparst und serialisiert. Dies ist eine enorme Verbesserung gegenüber URLSearchParams, das nur begrenzte Unterstützung für Array-ähnliche Strukturen und verschachtelte Daten bietet.

Zum Beispiel die Navigation zur folgenden Route

tsx
const link = (
  <Link
    to="/shop"
    search={{
      pageIndex: 3,
      includeCategories: ['electronics', 'gifts'],
      sortBy: 'price',
      desc: true,
    }}
  />
)
const link = (
  <Link
    to="/shop"
    search={{
      pageIndex: 3,
      includeCategories: ['electronics', 'gifts'],
      sortBy: 'price',
      desc: true,
    }}
  />
)

Führt zur folgenden URL

/shop?pageIndex=3&includeCategories=%5B%22electronics%22%2C%22gifts%22%5D&sortBy=price&desc=true
/shop?pageIndex=3&includeCategories=%5B%22electronics%22%2C%22gifts%22%5D&sortBy=price&desc=true

Beim Parsen dieser URL werden die Suchparameter korrekt in das folgende JSON umgewandelt

json
{
  "pageIndex": 3,
  "includeCategories": ["electronics", "gifts"],
  "sortBy": "price",
  "desc": true
}
{
  "pageIndex": 3,
  "includeCategories": ["electronics", "gifts"],
  "sortBy": "price",
  "desc": true
}

Wenn Sie es bemerkt haben, passieren hier ein paar Dinge

  • Die erste Ebene der Suchparameter ist flach und zeichenkettenbasiert, genau wie URLSearchParams.
  • Werte der ersten Ebene, die keine Zeichenketten sind, werden korrekt als tatsächliche Zahlen und Booleans beibehalten.
  • Verschachtelte Datenstrukturen werden automatisch in URL-sichere JSON-Zeichenketten umgewandelt

🧠 Es ist üblich, dass andere Tools davon ausgehen, dass Suchparameter immer flach und zeichenkettenbasiert sind, weshalb wir uns entschieden haben, auf der ersten Ebene URLSearchParam-konform zu bleiben. Das bedeutet letztendlich, dass TanStack Router Ihre verschachtelten Suchparameter zwar als JSON verwaltet, aber andere Tools immer noch in der Lage sein werden, in die URL zu schreiben und Parameter der ersten Ebene normal zu lesen.

Validieren und Typisieren von Suchparametern

Obwohl TanStack Router Suchparameter in zuverlässiges JSON parsen kann, stammen sie letztendlich von **einem benutzerorientierten Rohtext-Eingabefeld**. Ähnlich wie bei anderen Serialisierungsgrenzen bedeutet dies, dass sie vor der Verwendung durch die Anwendung validiert werden sollten, um ein Format zu erhalten, dem Ihre Anwendung vertrauen und auf das sie sich verlassen kann.

Eingabevalidierung + TypeScript!

TanStack Router bietet praktische APIs zum Validieren und Typisieren von Suchparametern. All dies beginnt mit der validateSearch-Option der Route

tsx
// /routes/shop.products.tsx

type ProductSearchSortOptions = 'newest' | 'oldest' | 'price'

type ProductSearch = {
  page: number
  filter: string
  sort: ProductSearchSortOptions
}

export const Route = createFileRoute('/shop/products')({
  validateSearch: (search: Record<string, unknown>): ProductSearch => {
    // validate and parse the search params into a typed state
    return {
      page: Number(search?.page ?? 1),
      filter: (search.filter as string) || '',
      sort: (search.sort as ProductSearchSortOptions) || 'newest',
    }
  },
})
// /routes/shop.products.tsx

type ProductSearchSortOptions = 'newest' | 'oldest' | 'price'

type ProductSearch = {
  page: number
  filter: string
  sort: ProductSearchSortOptions
}

export const Route = createFileRoute('/shop/products')({
  validateSearch: (search: Record<string, unknown>): ProductSearch => {
    // validate and parse the search params into a typed state
    return {
      page: Number(search?.page ?? 1),
      filter: (search.filter as string) || '',
      sort: (search.sort as ProductSearchSortOptions) || 'newest',
    }
  },
})

Im obigen Beispiel validieren wir die Suchparameter der Route und geben ein typisiertes ProductSearch-Objekt zurück. Dieses typisierte Objekt steht dann anderen Optionen dieser Route **und auch allen untergeordneten Routen** zur Verfügung!

Suchparameter validieren

Die Option validateSearch ist eine Funktion, die die JSON-geparsten (aber nicht validierten) Suchparameter als Record<string, unknown> erhält und ein typisiertes Objekt Ihrer Wahl zurückgibt. Es ist normalerweise am besten, sinnvolle Fallbacks für fehlerhafte oder unerwartete Suchparameter bereitzustellen, damit die Benutzererfahrung ununterbrochen bleibt.

Hier ist ein Beispiel

tsx
// /routes/shop.products.tsx

type ProductSearchSortOptions = 'newest' | 'oldest' | 'price'

type ProductSearch = {
  page: number
  filter: string
  sort: ProductSearchSortOptions
}

export const Route = createFileRoute('/shop/products')({
  validateSearch: (search: Record<string, unknown>): ProductSearch => {
    // validate and parse the search params into a typed state
    return {
      page: Number(search?.page ?? 1),
      filter: (search.filter as string) || '',
      sort: (search.sort as ProductSearchSortOptions) || 'newest',
    }
  },
})
// /routes/shop.products.tsx

type ProductSearchSortOptions = 'newest' | 'oldest' | 'price'

type ProductSearch = {
  page: number
  filter: string
  sort: ProductSearchSortOptions
}

export const Route = createFileRoute('/shop/products')({
  validateSearch: (search: Record<string, unknown>): ProductSearch => {
    // validate and parse the search params into a typed state
    return {
      page: Number(search?.page ?? 1),
      filter: (search.filter as string) || '',
      sort: (search.sort as ProductSearchSortOptions) || 'newest',
    }
  },
})

Hier ist ein Beispiel, das die Zod-Bibliothek verwendet (aber Sie können jede beliebige Validierungsbibliothek verwenden), um die Suchparameter in einem Schritt zu validieren und zu typisieren

tsx
// /routes/shop.products.tsx

import { z } from 'zod'

const productSearchSchema = z.object({
  page: z.number().catch(1),
  filter: z.string().catch(''),
  sort: z.enum(['newest', 'oldest', 'price']).catch('newest'),
})

type ProductSearch = z.infer<typeof productSearchSchema>

export const Route = createFileRoute('/shop/products')({
  validateSearch: (search) => productSearchSchema.parse(search),
})
// /routes/shop.products.tsx

import { z } from 'zod'

const productSearchSchema = z.object({
  page: z.number().catch(1),
  filter: z.string().catch(''),
  sort: z.enum(['newest', 'oldest', 'price']).catch('newest'),
})

type ProductSearch = z.infer<typeof productSearchSchema>

export const Route = createFileRoute('/shop/products')({
  validateSearch: (search) => productSearchSchema.parse(search),
})

Da validateSearch auch ein Objekt mit der Eigenschaft parse akzeptiert, kann dies verkürzt werden zu

tsx
validateSearch: productSearchSchema
validateSearch: productSearchSchema

Im obigen Beispiel haben wir den .catch()-Modifikator von Zod anstelle von .default() verwendet, um dem Benutzer keinen Fehler anzuzeigen, da wir fest davon überzeugt sind, dass Sie bei fehlerhaften Suchparametern die Benutzererfahrung nicht durch eine große Fehlermeldung unterbrechen möchten. Dennoch kann es Zeiten geben, in denen Sie **eine Fehlermeldung anzeigen möchten**. In diesem Fall können Sie anstelle von .catch() .default() verwenden.

Die zugrunde liegende Mechanik, warum dies funktioniert, beruht darauf, dass die Funktion validateSearch einen Fehler auslöst. Wenn ein Fehler ausgelöst wird, wird die Option onError der Route ausgelöst (und error.routerCode wird auf VALIDATE_SEARCH gesetzt und die errorComponent wird anstelle der component der Route gerendert, wo Sie den Suchparameterfehler beliebig behandeln können.

Adapter

Bei der Verwendung einer Bibliothek wie Zod zur Validierung von Suchparametern möchten Sie möglicherweise Suchparameter transformieren, bevor Sie die Suchparameter an die URL übergeben. Eine übliche zod- transform ist zum Beispiel default.

tsx
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'

const productSearchSchema = z.object({
  page: z.number().default(1),
  filter: z.string().default(''),
  sort: z.enum(['newest', 'oldest', 'price']).default('newest'),
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: productSearchSchema,
})
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'

const productSearchSchema = z.object({
  page: z.number().default(1),
  filter: z.string().default(''),
  sort: z.enum(['newest', 'oldest', 'price']).default('newest'),
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: productSearchSchema,
})

Es mag überraschend sein, dass bei der Navigation zu dieser Route search erforderlich ist. Der folgende Link wird einen Typfehler melden, da search fehlt.

tsx
<Link to="/shop/products" />
<Link to="/shop/products" />

Für Validierungsbibliotheken empfehlen wir die Verwendung von Adaptern, die die korrekten input- und output-Typen ableiten.

Zod

Ein Adapter wird für Zod bereitgestellt, der den korrekten input- und output-Typ durchleitet

tsx
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'

const productSearchSchema = z.object({
  page: z.number().default(1),
  filter: z.string().default(''),
  sort: z.enum(['newest', 'oldest', 'price']).default('newest'),
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: zodValidator(productSearchSchema),
})
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'

const productSearchSchema = z.object({
  page: z.number().default(1),
  filter: z.string().default(''),
  sort: z.enum(['newest', 'oldest', 'price']).default('newest'),
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: zodValidator(productSearchSchema),
})

Der wichtige Teil hier ist, dass die folgende Verwendung von Link search-Parameter nicht mehr benötigt.

tsx
<Link to="/shop/products" />
<Link to="/shop/products" />

Die Verwendung von catch hier überschreibt jedoch die Typen und macht page, filter und sort zu unknown, was zu Typverlust führt. Wir haben diesen Fall behandelt, indem wir eine generische Funktion fallback bereitgestellt haben, die die Typen beibehält, aber einen fallback-Wert liefert, wenn die Validierung fehlschlägt.

tsx
import { createFileRoute } from '@tanstack/react-router'
import { fallback, zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'

const productSearchSchema = z.object({
  page: fallback(z.number(), 1).default(1),
  filter: fallback(z.string(), '').default(''),
  sort: fallback(z.enum(['newest', 'oldest', 'price']), 'newest').default(
    'newest',
  ),
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: zodValidator(productSearchSchema),
})
import { createFileRoute } from '@tanstack/react-router'
import { fallback, zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'

const productSearchSchema = z.object({
  page: fallback(z.number(), 1).default(1),
  filter: fallback(z.string(), '').default(''),
  sort: fallback(z.enum(['newest', 'oldest', 'price']), 'newest').default(
    'newest',
  ),
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: zodValidator(productSearchSchema),
})

Wenn Sie also zu dieser Route navigieren, ist search optional und behält die korrekten Typen bei.

Obwohl nicht empfohlen, ist es auch möglich, input und output-Typen zu konfigurieren, falls der output-Typ genauer ist als der input-Typ.

tsx
const productSearchSchema = z.object({
  page: fallback(z.number(), 1).default(1),
  filter: fallback(z.string(), '').default(''),
  sort: fallback(z.enum(['newest', 'oldest', 'price']), 'newest').default(
    'newest',
  ),
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: zodValidator({
    schema: productSearchSchema,
    input: 'output',
    output: 'input',
  }),
})
const productSearchSchema = z.object({
  page: fallback(z.number(), 1).default(1),
  filter: fallback(z.string(), '').default(''),
  sort: fallback(z.enum(['newest', 'oldest', 'price']), 'newest').default(
    'newest',
  ),
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: zodValidator({
    schema: productSearchSchema,
    input: 'output',
    output: 'input',
  }),
})

Dies bietet Flexibilität bei der Wahl des Typs, den Sie für die Navigation und die Typen, die Sie für das Lesen von Suchparametern ableiten möchten.

Valibot

Warnung

Router erwartet, dass das Valibot 1.0-Paket installiert ist.

Bei Verwendung von Valibot ist kein Adapter erforderlich, um sicherzustellen, dass die korrekten input- und output-Typen für Navigation und das Lesen von Suchparametern verwendet werden. Dies liegt daran, dass valibot Standard Schema implementiert.

tsx
import { createFileRoute } from '@tanstack/react-router'
import * as v from 'valibot'

const productSearchSchema = v.object({
  page: v.optional(v.fallback(v.number(), 1), 1),
  filter: v.optional(v.fallback(v.string(), ''), ''),
  sort: v.optional(
    v.fallback(v.picklist(['newest', 'oldest', 'price']), 'newest'),
    'newest',
  ),
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: productSearchSchema,
})
import { createFileRoute } from '@tanstack/react-router'
import * as v from 'valibot'

const productSearchSchema = v.object({
  page: v.optional(v.fallback(v.number(), 1), 1),
  filter: v.optional(v.fallback(v.string(), ''), ''),
  sort: v.optional(
    v.fallback(v.picklist(['newest', 'oldest', 'price']), 'newest'),
    'newest',
  ),
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: productSearchSchema,
})

Arktype

Warnung

Router erwartet, dass das Arktype 2.0-rc-Paket installiert ist.

Bei Verwendung von ArkType ist kein Adapter erforderlich, um sicherzustellen, dass die korrekten input- und output-Typen für Navigation und das Lesen von Suchparametern verwendet werden. Dies liegt daran, dass ArkType Standard Schema implementiert.

tsx
import { createFileRoute } from '@tanstack/react-router'
import { type } from 'arktype'

const productSearchSchema = type({
  page: 'number = 1',
  filter: 'string = ""',
  sort: '"newest" | "oldest" | "price" = "newest"',
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: productSearchSchema,
})
import { createFileRoute } from '@tanstack/react-router'
import { type } from 'arktype'

const productSearchSchema = type({
  page: 'number = 1',
  filter: 'string = ""',
  sort: '"newest" | "oldest" | "price" = "newest"',
})

export const Route = createFileRoute('/shop/products/')({
  validateSearch: productSearchSchema,
})

Effect/Schema

Bei Verwendung von Effect/Schema ist kein Adapter erforderlich, um sicherzustellen, dass die korrekten input- und output-Typen für Navigation und das Lesen von Suchparametern verwendet werden. Dies liegt daran, dass Effect/Schema Standard Schema implementiert.

tsx
import { createFileRoute } from '@tanstack/react-router'
import { Schema as S } from 'effect'

const productSearchSchema = S.standardSchemaV1(
  S.Struct({
    page: S.NumberFromString.pipe(
      S.optional,
      S.withDefaults({
        constructor: () => 1,
        decoding: () => 1,
      }),
    ),
    filter: S.String.pipe(
      S.optional,
      S.withDefaults({
        constructor: () => '',
        decoding: () => '',
      }),
    ),
    sort: S.Literal('newest', 'oldest', 'price').pipe(
      S.optional,
      S.withDefaults({
        constructor: () => 'newest' as const,
        decoding: () => 'newest' as const,
      }),
    ),
  }),
)

export const Route = createFileRoute('/shop/products/')({
  validateSearch: productSearchSchema,
})
import { createFileRoute } from '@tanstack/react-router'
import { Schema as S } from 'effect'

const productSearchSchema = S.standardSchemaV1(
  S.Struct({
    page: S.NumberFromString.pipe(
      S.optional,
      S.withDefaults({
        constructor: () => 1,
        decoding: () => 1,
      }),
    ),
    filter: S.String.pipe(
      S.optional,
      S.withDefaults({
        constructor: () => '',
        decoding: () => '',
      }),
    ),
    sort: S.Literal('newest', 'oldest', 'price').pipe(
      S.optional,
      S.withDefaults({
        constructor: () => 'newest' as const,
        decoding: () => 'newest' as const,
      }),
    ),
  }),
)

export const Route = createFileRoute('/shop/products/')({
  validateSearch: productSearchSchema,
})

Suchparameter lesen

Sobald Ihre Suchparameter validiert und typisiert sind, sind Sie endlich bereit, mit dem Lesen und Schreiben zu beginnen. In TanStack Router gibt es dafür mehrere Möglichkeiten, lassen Sie uns diese untersuchen.

Verwendung von Suchparametern in Loadern

Bitte lesen Sie den Abschnitt Suchparameter in Loadern für weitere Informationen darüber, wie Suchparameter in Loadern mit der Option loaderDeps gelesen werden.

Suchparameter werden von übergeordneten Routen geerbt

Die Suchparameter und Typen der Eltern werden beim Abstieg im Routenbaum zusammengeführt, sodass untergeordnete Routen auch Zugriff auf die Suchparameter ihrer Eltern haben.

  • shop.products.tsx
tsx
const productSearchSchema = z.object({
  page: z.number().catch(1),
  filter: z.string().catch(''),
  sort: z.enum(['newest', 'oldest', 'price']).catch('newest'),
})

type ProductSearch = z.infer<typeof productSearchSchema>

export const Route = createFileRoute('/shop/products')({
  validateSearch: productSearchSchema,
})
const productSearchSchema = z.object({
  page: z.number().catch(1),
  filter: z.string().catch(''),
  sort: z.enum(['newest', 'oldest', 'price']).catch('newest'),
})

type ProductSearch = z.infer<typeof productSearchSchema>

export const Route = createFileRoute('/shop/products')({
  validateSearch: productSearchSchema,
})
  • shop.products.$productId.tsx
tsx
export const Route = createFileRoute('/shop/products/$productId')({
  beforeLoad: ({ search }) => {
    search
    // ^? ProductSearch ✅
  },
})
export const Route = createFileRoute('/shop/products/$productId')({
  beforeLoad: ({ search }) => {
    search
    // ^? ProductSearch ✅
  },
})

Suchparameter in Komponenten

Sie können auf die validierten Suchparameter Ihrer Route in der component Ihrer Route über den Hook useSearch zugreifen.

tsx
// /routes/shop.products.tsx

export const Route = createFileRoute('/shop/products')({
  validateSearch: productSearchSchema,
})

const ProductList = () => {
  const { page, filter, sort } = Route.useSearch()

  return <div>...</div>
}
// /routes/shop.products.tsx

export const Route = createFileRoute('/shop/products')({
  validateSearch: productSearchSchema,
})

const ProductList = () => {
  const { page, filter, sort } = Route.useSearch()

  return <div>...</div>
}

Tipp

Wenn Ihre Komponente Code-splitting-fähig ist, können Sie die getRouteApi-Funktion verwenden, um den Import der Route-Konfiguration zu vermeiden und Zugriff auf den typisierten useSearch()-Hook zu erhalten.

Suchparameter außerhalb von Routenkomponenten

Sie können auf die validierten Suchparameter Ihrer Route überall in Ihrer App mit dem Hook useSearch zugreifen. Durch die Übergabe der ID/des Pfads from Ihrer Ursprungsroute erhalten Sie eine noch bessere Typsicherheit.

tsx
// /routes/shop.products.tsx
export const Route = createFileRoute('/shop/products')({
  validateSearch: productSearchSchema,
  // ...
})

// Somewhere else...

// /components/product-list-sidebar.tsx
const routeApi = getRouteApi('/shop/products')

const ProductList = () => {
  const routeSearch = routeApi.useSearch()

  // OR

  const { page, filter, sort } = useSearch({
    from: Route.fullPath,
  })

  return <div>...</div>
}
// /routes/shop.products.tsx
export const Route = createFileRoute('/shop/products')({
  validateSearch: productSearchSchema,
  // ...
})

// Somewhere else...

// /components/product-list-sidebar.tsx
const routeApi = getRouteApi('/shop/products')

const ProductList = () => {
  const routeSearch = routeApi.useSearch()

  // OR

  const { page, filter, sort } = useSearch({
    from: Route.fullPath,
  })

  return <div>...</div>
}

Alternativ können Sie die Typsicherheit lockern und ein optionales search-Objekt erhalten, indem Sie strict: false übergeben.

tsx
function ProductList() {
  const search = useSearch({
    strict: false,
  })
  // {
  //   page: number | undefined
  //   filter: string | undefined
  //   sort: 'newest' | 'oldest' | 'price' | undefined
  // }

  return <div>...</div>
}
function ProductList() {
  const search = useSearch({
    strict: false,
  })
  // {
  //   page: number | undefined
  //   filter: string | undefined
  //   sort: 'newest' | 'oldest' | 'price' | undefined
  // }

  return <div>...</div>
}

Suchparameter schreiben

Nachdem Sie nun gelernt haben, wie Sie die Suchparameter Ihrer Route lesen, werden Sie erfreut sein zu erfahren, dass Sie bereits die primären APIs zum Ändern und Aktualisieren gesehen haben. Lassen Sie uns uns kurz daran erinnern.

Der beste Weg, Suchparameter zu aktualisieren, ist die Verwendung der search-Eigenschaft der <Link />-Komponente.

Wenn die Suche für die aktuelle Seite aktualisiert werden soll und die from-Eigenschaft angegeben ist, kann die to-Eigenschaft weggelassen werden.
Hier ist ein Beispiel

tsx
// /routes/shop.products.tsx
export const Route = createFileRoute('/shop/products')({
  validateSearch: productSearchSchema,
})

const ProductList = () => {
  return (
    <div>
      <Link from={Route.fullPath} search={(prev) => ({ page: prev.page + 1 })}>
        Next Page
      </Link>
    </div>
  )
}
// /routes/shop.products.tsx
export const Route = createFileRoute('/shop/products')({
  validateSearch: productSearchSchema,
})

const ProductList = () => {
  return (
    <div>
      <Link from={Route.fullPath} search={(prev) => ({ page: prev.page + 1 })}>
        Next Page
      </Link>
    </div>
  )
}

Wenn Sie die Suchparameter in einer generischen Komponente aktualisieren möchten, die auf mehreren Routen gerendert wird, kann die Angabe von from schwierig sein.

In diesem Szenario können Sie to="." setzen, was Ihnen Zugriff auf lose typisierte Suchparameter gewährt.
Hier ist ein Beispiel, das dies veranschaulicht

tsx
// `page` is a search param that is defined in the __root route and hence available on all routes.
const PageSelector = () => {
  return (
    <div>
      <Link to="." search={(prev) => ({ ...prev, page: prev.page + 1 })}>
        Next Page
      </Link>
    </div>
  )
}
// `page` is a search param that is defined in the __root route and hence available on all routes.
const PageSelector = () => {
  return (
    <div>
      <Link to="." search={(prev) => ({ ...prev, page: prev.page + 1 })}>
        Next Page
      </Link>
    </div>
  )
}

Wenn die generische Komponente nur in einem bestimmten Unterbaum des Routenbaums gerendert wird, können Sie diesen Unterbaum mit from angeben. Hier können Sie to='.' weglassen, wenn Sie möchten.

tsx
// `page` is a search param that is defined in the /posts route and hence available on all of its child routes.
const PageSelector = () => {
  return (
    <div>
      <Link
        from="/posts"
        to="."
        search={(prev) => ({ ...prev, page: prev.page + 1 })}
      >
        Next Page
      </Link>
    </div>
  )
// `page` is a search param that is defined in the /posts route and hence available on all of its child routes.
const PageSelector = () => {
  return (
    <div>
      <Link
        from="/posts"
        to="."
        search={(prev) => ({ ...prev, page: prev.page + 1 })}
      >
        Next Page
      </Link>
    </div>
  )

useNavigate(), navigate({ search })

Die Funktion navigate akzeptiert auch eine search-Option, die genauso funktioniert wie die search-Eigenschaft auf <Link />.

tsx
// /routes/shop.products.tsx
export const Route = createFileRoute('/shop/products/$productId')({
  validateSearch: productSearchSchema,
})

const ProductList = () => {
  const navigate = useNavigate({ from: Route.fullPath })

  return (
    <div>
      <button
        onClick={() => {
          navigate({
            search: (prev) => ({ page: prev.page + 1 }),
          })
        }}
      >
        Next Page
      </button>
    </div>
  )
}
// /routes/shop.products.tsx
export const Route = createFileRoute('/shop/products/$productId')({
  validateSearch: productSearchSchema,
})

const ProductList = () => {
  const navigate = useNavigate({ from: Route.fullPath })

  return (
    <div>
      <button
        onClick={() => {
          navigate({
            search: (prev) => ({ page: prev.page + 1 }),
          })
        }}
      >
        Next Page
      </button>
    </div>
  )
}

router.navigate({ search })

Die Funktion router.navigate funktioniert genauso wie der obige Hook/die Hook/Funktion useNavigate/navigate.

Die Komponente <Navigate search /> funktioniert genauso wie der obige Hook/die Hook/Funktion useNavigate/navigate, akzeptiert aber ihre Optionen als Props anstelle eines Funktionsarguments.

Suchparameter mit Such-Middlewares transformieren

Beim Erstellen von Link-hrefs ist standardmäßig nur die search-Eigenschaft eines <Link> für den Abfragezeichenketten-Teil relevant.

TanStack Router bietet eine Möglichkeit, Suchparameter zu manipulieren, bevor der href generiert wird, über **Such-Middlewares**. Such-Middlewares sind Funktionen, die die Suchparameter beim Generieren neuer Links für eine Route oder deren Nachkommen transformieren. Sie werden auch bei der Navigation nach der Suchvalidierung ausgeführt, um die Manipulation der Abfragezeichenkette zu ermöglichen.

Das folgende Beispiel zeigt, wie sichergestellt wird, dass für **jeden** Link, der erstellt wird, der Suchparameter rootValue hinzugefügt wird, *wenn* er Teil der aktuellen Suchparameter ist. Wenn ein Link rootValue innerhalb von search angibt, wird dieser Wert für die Link-Erstellung verwendet.

tsx
import { z } from 'zod'
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'

const searchSchema = z.object({
  rootValue: z.string().optional(),
})

export const Route = createRootRoute({
  validateSearch: zodValidator(searchSchema),
  search: {
    middlewares: [
      ({ search, next }) => {
        const result = next(search)
        return {
          rootValue: search.rootValue,
          ...result,
        }
      },
    ],
  },
})
import { z } from 'zod'
import { createFileRoute } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'

const searchSchema = z.object({
  rootValue: z.string().optional(),
})

export const Route = createRootRoute({
  validateSearch: zodValidator(searchSchema),
  search: {
    middlewares: [
      ({ search, next }) => {
        const result = next(search)
        return {
          rootValue: search.rootValue,
          ...result,
        }
      },
    ],
  },
})

Da dieser spezielle Anwendungsfall recht häufig vorkommt, bietet TanStack Router eine generische Implementierung zur Beibehaltung von Suchparametern über retainSearchParams.

tsx
import { z } from 'zod'
import { createFileRoute, retainSearchParams } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'

const searchSchema = z.object({
  rootValue: z.string().optional(),
})

export const Route = createRootRoute({
  validateSearch: zodValidator(searchSchema),
  search: {
    middlewares: [retainSearchParams(['rootValue'])],
  },
})
import { z } from 'zod'
import { createFileRoute, retainSearchParams } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'

const searchSchema = z.object({
  rootValue: z.string().optional(),
})

export const Route = createRootRoute({
  validateSearch: zodValidator(searchSchema),
  search: {
    middlewares: [retainSearchParams(['rootValue'])],
  },
})

Ein weiterer häufiger Anwendungsfall ist das Entfernen von Suchparametern aus Links, wenn ihr Standardwert gesetzt ist. TanStack Router bietet eine generische Implementierung für diesen Anwendungsfall über stripSearchParams.

tsx
import { z } from 'zod'
import { createFileRoute, stripSearchParams } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'

const defaultValues = {
  one: 'abc',
  two: 'xyz',
}

const searchSchema = z.object({
  one: z.string().default(defaultValues.one),
  two: z.string().default(defaultValues.two),
})

export const Route = createFileRoute('/hello')({
  validateSearch: zodValidator(searchSchema),
  search: {
    // strip default values
    middlewares: [stripSearchParams(defaultValues)],
  },
})
import { z } from 'zod'
import { createFileRoute, stripSearchParams } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'

const defaultValues = {
  one: 'abc',
  two: 'xyz',
}

const searchSchema = z.object({
  one: z.string().default(defaultValues.one),
  two: z.string().default(defaultValues.two),
})

export const Route = createFileRoute('/hello')({
  validateSearch: zodValidator(searchSchema),
  search: {
    // strip default values
    middlewares: [stripSearchParams(defaultValues)],
  },
})

Mehrere Middlewares können verkettet werden. Das folgende Beispiel zeigt, wie sowohl retainSearchParams als auch stripSearchParams kombiniert werden.

tsx
import {
  Link,
  createFileRoute,
  retainSearchParams,
  stripSearchParams,
} from '@tanstack/react-router'
import { z } from 'zod'
import { zodValidator } from '@tanstack/zod-adapter'

const defaultValues = ['foo', 'bar']

export const Route = createFileRoute('/search')({
  validateSearch: zodValidator(
    z.object({
      retainMe: z.string().optional(),
      arrayWithDefaults: z.string().array().default(defaultValues),
      required: z.string(),
    }),
  ),
  search: {
    middlewares: [
      retainSearchParams(['retainMe']),
      stripSearchParams({ arrayWithDefaults: defaultValues }),
    ],
  },
})
import {
  Link,
  createFileRoute,
  retainSearchParams,
  stripSearchParams,
} from '@tanstack/react-router'
import { z } from 'zod'
import { zodValidator } from '@tanstack/zod-adapter'

const defaultValues = ['foo', 'bar']

export const Route = createFileRoute('/search')({
  validateSearch: zodValidator(
    z.object({
      retainMe: z.string().optional(),
      arrayWithDefaults: z.string().array().default(defaultValues),
      required: z.string(),
    }),
  ),
  search: {
    middlewares: [
      retainSearchParams(['retainMe']),
      stripSearchParams({ arrayWithDefaults: defaultValues }),
    ],
  },
})
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.