Navigation

Alles ist relativ

Glauben Sie es oder nicht, jede Navigation innerhalb einer App ist relativ, auch wenn Sie keine explizite relative Pfadsyntax verwenden (../../somewhere). Jedes Mal, wenn ein Link angeklickt wird oder ein imperativer Navigationsaufruf erfolgt, haben Sie immer einen Ursprungspfad und einen Zielpfad, was bedeutet, dass Sie von einer Route zu einer anderen navigieren.

TanStack Router berücksichtigt dieses konstante Konzept der relativen Navigation für jede Navigation. Daher werden Sie in der API ständig zwei Eigenschaften sehen:

  • from - Der Ursprungs-Routenpfad
  • to - Der Ziel-Routenpfad

⚠️ Wenn kein from Routenpfad angegeben ist, geht der Router davon aus, dass Sie von der Root-Route / navigieren und vervollständigt nur absolute Pfade. Schließlich müssen Sie wissen, woher Sie kommen, um zu wissen, wohin Sie gehen 😉.

Gemeinsame Navigations-API

Jede Navigations- und Routenabgleich-API in TanStack Router verwendet dieselbe Kernschnittstelle mit geringfügigen Unterschieden, abhängig von der API. Das bedeutet, dass Sie Navigation und Routenabgleich einmal lernen und die gleiche Syntax und Konzepte in der gesamten Bibliothek verwenden können.

ToOptions Schnittstelle

Dies ist die Kern-Schnittstelle ToOptions, die in jeder Navigations- und Routenabgleich-API verwendet wird.

ts
type ToOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = {
  // `from` is an optional route ID or path. If it is not supplied, only absolute paths will be auto-completed and type-safe. It's common to supply the route.fullPath of the origin route you are rendering from for convenience. If you don't know the origin route, leave this empty and work with absolute paths or unsafe relative paths.
  from?: string
  // `to` can be an absolute route path or a relative path from the `from` option to a valid route path. ⚠️ Do not interpolate path params, hash or search params into the `to` options. Use the `params`, `search`, and `hash` options instead.
  to: string
  // `params` is either an object of path params to interpolate into the `to` option or a function that supplies the previous params and allows you to return new ones. This is the only way to interpolate dynamic parameters into the final URL. Depending on the `from` and `to` route, you may need to supply none, some or all of the path params. TypeScript will notify you of the required params if there are any.
  params:
    | Record<string, unknown>
    | ((prevParams: Record<string, unknown>) => Record<string, unknown>)
  // `search` is either an object of query params or a function that supplies the previous search and allows you to return new ones. Depending on the `from` and `to` route, you may need to supply none, some or all of the query params. TypeScript will notify you of the required search params if there are any.
  search:
    | Record<string, unknown>
    | ((prevSearch: Record<string, unknown>) => Record<string, unknown>)
  // `hash` is either a string or a function that supplies the previous hash and allows you to return a new one.
  hash?: string | ((prevHash: string) => string)
  // `state` is either an object of state or a function that supplies the previous state and allows you to return a new one. State is stored in the history API and can be useful for passing data between routes that you do not want to permanently store in URL search params.
  state?:
    | Record<string, any>
    | ((prevState: Record<string, unknown>) => Record<string, unknown>)
}
type ToOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = {
  // `from` is an optional route ID or path. If it is not supplied, only absolute paths will be auto-completed and type-safe. It's common to supply the route.fullPath of the origin route you are rendering from for convenience. If you don't know the origin route, leave this empty and work with absolute paths or unsafe relative paths.
  from?: string
  // `to` can be an absolute route path or a relative path from the `from` option to a valid route path. ⚠️ Do not interpolate path params, hash or search params into the `to` options. Use the `params`, `search`, and `hash` options instead.
  to: string
  // `params` is either an object of path params to interpolate into the `to` option or a function that supplies the previous params and allows you to return new ones. This is the only way to interpolate dynamic parameters into the final URL. Depending on the `from` and `to` route, you may need to supply none, some or all of the path params. TypeScript will notify you of the required params if there are any.
  params:
    | Record<string, unknown>
    | ((prevParams: Record<string, unknown>) => Record<string, unknown>)
  // `search` is either an object of query params or a function that supplies the previous search and allows you to return new ones. Depending on the `from` and `to` route, you may need to supply none, some or all of the query params. TypeScript will notify you of the required search params if there are any.
  search:
    | Record<string, unknown>
    | ((prevSearch: Record<string, unknown>) => Record<string, unknown>)
  // `hash` is either a string or a function that supplies the previous hash and allows you to return a new one.
  hash?: string | ((prevHash: string) => string)
  // `state` is either an object of state or a function that supplies the previous state and allows you to return a new one. State is stored in the history API and can be useful for passing data between routes that you do not want to permanently store in URL search params.
  state?:
    | Record<string, any>
    | ((prevState: Record<string, unknown>) => Record<string, unknown>)
}

🧠 Jedes Routenobjekt hat eine to-Eigenschaft, die als to für jede Navigations- oder Routenabgleich-API verwendet werden kann. Wo immer möglich, ermöglicht Ihnen dies, reine Strings zu vermeiden und stattdessen typsichere Routenreferenzen zu verwenden.

tsx
import { Route as aboutRoute } from './routes/about.tsx'

function Comp() {
  return <Link to={aboutRoute.to}>About</Link>
}
import { Route as aboutRoute } from './routes/about.tsx'

function Comp() {
  return <Link to={aboutRoute.to}>About</Link>
}

Dies ist die Kern-Schnittstelle NavigateOptions, die ToOptions erweitert. Jede API, die tatsächlich eine Navigation durchführt, verwendet diese Schnittstelle.

ts
export type NavigateOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = ToOptions<TRouteTree, TFrom, TTo> & {
  // `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.
  replace?: boolean
  // `resetScroll` is a boolean that determines whether scroll position will be reset to 0,0 after the location is committed to browser history.
  resetScroll?: boolean
  // `hashScrollIntoView` is a boolean or object that determines whether an id matching the hash will be scrolled into view after the location is committed to history.
  hashScrollIntoView?: boolean | ScrollIntoViewOptions
  // `viewTransition` is either a boolean or function that determines if and how the browser will call document.startViewTransition() when navigating.
  viewTransition?: boolean | ViewTransitionOptions
  // `ignoreBlocker` is a boolean that determines if navigation should ignore any blockers that might prevent it.
  ignoreBlocker?: boolean
  // `reloadDocument` is a boolean that determines if navigation to a route inside of router will trigger a full page load instead of the traditional SPA navigation.
  reloadDocument?: boolean
  // `href` is a string that can be used in place of `to` to navigate to a full built href, e.g. pointing to an external target.
  href?: string
}
export type NavigateOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = ToOptions<TRouteTree, TFrom, TTo> & {
  // `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.
  replace?: boolean
  // `resetScroll` is a boolean that determines whether scroll position will be reset to 0,0 after the location is committed to browser history.
  resetScroll?: boolean
  // `hashScrollIntoView` is a boolean or object that determines whether an id matching the hash will be scrolled into view after the location is committed to history.
  hashScrollIntoView?: boolean | ScrollIntoViewOptions
  // `viewTransition` is either a boolean or function that determines if and how the browser will call document.startViewTransition() when navigating.
  viewTransition?: boolean | ViewTransitionOptions
  // `ignoreBlocker` is a boolean that determines if navigation should ignore any blockers that might prevent it.
  ignoreBlocker?: boolean
  // `reloadDocument` is a boolean that determines if navigation to a route inside of router will trigger a full page load instead of the traditional SPA navigation.
  reloadDocument?: boolean
  // `href` is a string that can be used in place of `to` to navigate to a full built href, e.g. pointing to an external target.
  href?: string
}

LinkOptions Schnittstelle

Überall dort, wo ein tatsächlicher <a>-Tag verwendet wird, ist die Schnittstelle LinkOptions, die NavigateOptions erweitert, verfügbar.

tsx
export type LinkOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = NavigateOptions<TRouteTree, TFrom, TTo> & {
  // The standard anchor tag target attribute
  target?: HTMLAnchorElement['target']
  // Defaults to `{ exact: false, includeHash: false }`
  activeOptions?: {
    exact?: boolean
    includeHash?: boolean
    includeSearch?: boolean
    explicitUndefined?: boolean
  }
  // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.
  preload?: false | 'intent'
  // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.
  preloadDelay?: number
  // If true, will render the link without the href attribute
  disabled?: boolean
}
export type LinkOptions<
  TRouteTree extends AnyRoute = AnyRoute,
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
> = NavigateOptions<TRouteTree, TFrom, TTo> & {
  // The standard anchor tag target attribute
  target?: HTMLAnchorElement['target']
  // Defaults to `{ exact: false, includeHash: false }`
  activeOptions?: {
    exact?: boolean
    includeHash?: boolean
    includeSearch?: boolean
    explicitUndefined?: boolean
  }
  // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.
  preload?: false | 'intent'
  // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.
  preloadDelay?: number
  // If true, will render the link without the href attribute
  disabled?: boolean
}

Mit relativer Navigation und allen Schnittstellen im Hinterkopf, sprechen wir nun über die verschiedenen Navigations-APIs, die Ihnen zur Verfügung stehen.

  • Die <Link> Komponente
    • Generiert ein tatsächliches <a>-Tag mit einem gültigen href, das angeklickt oder sogar mit Cmd/Strg + Klick in einem neuen Tab geöffnet werden kann.
  • Der useNavigate() Hook
    • Wenn möglich, sollte die Link-Komponente für die Navigation verwendet werden, aber manchmal müssen Sie als Ergebnis eines Nebeneffekts imperativ navigieren. useNavigate gibt eine Funktion zurück, die aufgerufen werden kann, um eine sofortige clientseitige Navigation durchzuführen.
  • Die <Navigate> Komponente
    • Rendert nichts und führt eine sofortige clientseitige Navigation durch.
  • Die Methode Router.navigate()
    • Dies ist die leistungsstärkste Navigations-API in TanStack Router. Ähnlich wie useNavigate navigiert sie imperativ, ist aber überall dort verfügbar, wo Sie Zugriff auf Ihren Router haben.

⚠️ Keine dieser APIs ist ein Ersatz für serverseitige Weiterleitungen. Wenn Sie einen Benutzer sofort von einer Route zu einer anderen weiterleiten müssen, bevor Ihre Anwendung gemountet wird, verwenden Sie stattdessen eine serverseitige Weiterleitung anstelle einer clientseitigen Navigation.

Die Link-Komponente ist die gebräuchlichste Methode, um innerhalb einer App zu navigieren. Sie rendert ein tatsächliches <a>-Tag mit einem gültigen href-Attribut, das angeklickt oder sogar mit Cmd/Strg + Klick in einem neuen Tab geöffnet werden kann. Sie unterstützt auch alle normalen <a>-Attribute, einschließlich target, um Links in neuen Fenstern zu öffnen usw.

Zusätzlich zur Schnittstelle LinkOptions unterstützt die Link-Komponente auch die folgenden Props:

tsx
export type LinkProps<
  TFrom extends RoutePaths<RegisteredRouter['routeTree']> | string = string,
  TTo extends string = '',
> = LinkOptions<RegisteredRouter['routeTree'], TFrom, TTo> & {
  // A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
  activeProps?:
    | FrameworkHTMLAnchorTagAttributes
    | (() => FrameworkHTMLAnchorAttributes)
  // A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
  inactiveProps?:
    | FrameworkHTMLAnchorAttributes
    | (() => FrameworkHTMLAnchorAttributes)
}
export type LinkProps<
  TFrom extends RoutePaths<RegisteredRouter['routeTree']> | string = string,
  TTo extends string = '',
> = LinkOptions<RegisteredRouter['routeTree'], TFrom, TTo> & {
  // A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
  activeProps?:
    | FrameworkHTMLAnchorTagAttributes
    | (() => FrameworkHTMLAnchorAttributes)
  // A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
  inactiveProps?:
    | FrameworkHTMLAnchorAttributes
    | (() => FrameworkHTMLAnchorAttributes)
}

Erstellen wir einen einfachen statischen Link!

tsx
import { Link } from '@tanstack/solid-router'

const link = <Link to="/about">About</Link>
import { Link } from '@tanstack/solid-router'

const link = <Link to="/about">About</Link>

Dynamische Links sind Links, die dynamische Segmente enthalten. Zum Beispiel könnte ein Link zu einem Blogbeitrag so aussehen:

tsx
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
  >
    Blog Post
  </Link>
)
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
  >
    Blog Post
  </Link>
)

Beachten Sie, dass dynamische Segmentparameter normalerweise string-Werte sind, aber sie können auch jeder andere Typ sein, zu dem Sie sie in Ihren Routenoptionen parsen. In beiden Fällen wird der Typ zur Kompilierzeit überprüft, um sicherzustellen, dass Sie den richtigen Typ übergeben.

Standardmäßig sind alle Links absolut, es sei denn, ein from-Routenpfad wird angegeben. Das bedeutet, dass der obige Link unabhängig davon, auf welcher Route Sie sich gerade befinden, immer zur Route /about navigiert.

Relative Links können mit einem from-Routenpfad kombiniert werden. Wenn kein From-Routenpfad angegeben ist, werden relative Pfade standardmäßig an den aktuellen aktiven Speicherort aufgelöst.

tsx
const postIdRoute = createRoute({
  path: '/blog/post/$postId',
})

const link = (
  <Link from={postIdRoute.fullPath} to="../categories">
    Categories
  </Link>
)
const postIdRoute = createRoute({
  path: '/blog/post/$postId',
})

const link = (
  <Link from={postIdRoute.fullPath} to="../categories">
    Categories
  </Link>
)

Wie oben gezeigt, ist es üblich, den route.fullPath als from-Routenpfad anzugeben. Dies liegt daran, dass route.fullPath eine Referenz ist, die sich aktualisiert, wenn Sie Ihre Anwendung refaktorisieren. Manchmal ist es jedoch nicht möglich, die Route direkt zu importieren. In diesem Fall ist es in Ordnung, den Routenpfad direkt als String anzugeben. Er wird weiterhin wie üblich typsicher überprüft!

Spezielle relative Pfade: "." und ".."

Sehr oft möchten Sie den aktuellen Speicherort oder einen anderen from-Pfad neu laden, z. B. um die Loader auf der aktuellen und/oder übergeordneten Routen erneut auszuführen, oder vielleicht zu einer übergeordneten Route zurückzukehren. Dies kann erreicht werden, indem ein to-Routenpfad von "." angegeben wird, was den aktuellen Speicherort oder den angegebenen from-Pfad neu lädt.

Ein weiterer häufiger Bedarf ist die Navigation eine Route zurück relativ zum aktuellen Speicherort oder einem anderen Pfad. Durch die Angabe eines to-Routenpfads von ".." wird die Navigation zur ersten übergeordneten Route vor dem aktuellen Speicherort aufgelöst.

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

function PostComponent() {
  return (
    <div>
      <Link to=".">Reload the current route of /posts/$postId</Link>
      <Link to="..">Navigate back to /posts</Link>
      // the below are all equivalent
      <Link to="/posts">Navigate back to /posts</Link>
      <Link from="/posts" to=".">
        Navigate back to /posts
      </Link>
      // the below are all equivalent
      <Link to="/">Navigate to root</Link>
      <Link from="/posts" to="..">
        Navigate to root
      </Link>
    </div>
  )
}
export const Route = createFileRoute('/posts/$postId')({
  component: PostComponent,
})

function PostComponent() {
  return (
    <div>
      <Link to=".">Reload the current route of /posts/$postId</Link>
      <Link to="..">Navigate back to /posts</Link>
      // the below are all equivalent
      <Link to="/posts">Navigate back to /posts</Link>
      <Link from="/posts" to=".">
        Navigate back to /posts
      </Link>
      // the below are all equivalent
      <Link to="/">Navigate to root</Link>
      <Link from="/posts" to="..">
        Navigate to root
      </Link>
    </div>
  )
}

Suchparameter sind eine großartige Möglichkeit, zusätzliche Kontexte zu einer Route bereitzustellen. Zum Beispiel möchten Sie vielleicht eine Suchanfrage für eine Suchseite bereitstellen.

tsx
const link = (
  <Link
    to="/search"
    search={{
      query: 'tanstack',
    }}
  >
    Search
  </Link>
)
const link = (
  <Link
    to="/search"
    search={{
      query: 'tanstack',
    }}
  >
    Search
  </Link>
)

Es ist auch üblich, einen einzelnen Suchparameter zu aktualisieren, ohne weitere Informationen über die vorhandene Route anzugeben. Zum Beispiel möchten Sie vielleicht die Seitenzahl eines Suchergebnisses aktualisieren.

tsx
const link = (
  <Link
    to="."
    search={(prev) => ({
      ...prev,
      page: prev.page + 1,
    })}
  >
    Next Page
  </Link>
)
const link = (
  <Link
    to="."
    search={(prev) => ({
      ...prev,
      page: prev.page + 1,
    })}
  >
    Next Page
  </Link>
)

Typsicherheit für Suchparameter

Suchparameter sind ein hochdynamischer Mechanismus zur Zustandsverwaltung. Daher ist es wichtig sicherzustellen, dass Sie die richtigen Typen an Ihre Suchparameter übergeben. Wir werden in einem späteren Abschnitt detailliert darauf eingehen, wie Sie die Typsicherheit von Suchparametern und andere großartige Funktionen validieren und sicherstellen!

Hash-Links sind eine großartige Möglichkeit, auf einen bestimmten Abschnitt einer Seite zu verlinken. Zum Beispiel möchten Sie vielleicht auf einen bestimmten Abschnitt eines Blogbeitrags verlinken.

tsx
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
    hash="section-1"
  >
    Section 1
  </Link>
)
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
    hash="section-1"
  >
    Section 1
  </Link>
)

Optionale Pfadparameter bieten flexible Navigationsmuster, bei denen Sie Parameter nach Bedarf einschließen oder weglassen können. Optionale Parameter verwenden die Syntax {-$paramName} und bieten eine feingranulare Kontrolle über die URL-Struktur.

Parametervererbung vs. Entfernung

Bei der Navigation mit optionalen Parametern haben Sie zwei Hauptstrategien:

Aktuelle Parameter vererben Verwenden Sie params: {}, um alle aktuellen Routenparameter zu vererben.

tsx
// Inherits current route parameters
<Link to="/posts/{-$category}" params={{}}>
  All Posts
</Link>
// Inherits current route parameters
<Link to="/posts/{-$category}" params={{}}>
  All Posts
</Link>

Parameter entfernen
Setzen Sie Parameter auf undefined, um sie explizit zu entfernen.

tsx
// Removes the category parameter
<Link to="/posts/{-$category}" params={{ category: undefined }}>
  All Posts
</Link>
// Removes the category parameter
<Link to="/posts/{-$category}" params={{ category: undefined }}>
  All Posts
</Link>

Einfache optionale Parameter-Navigation

tsx
// Navigate with optional parameter
<Link
  to="/posts/{-$category}"
  params={{ category: 'tech' }}
>
  Tech Posts
</Link>

// Navigate without optional parameter
<Link
  to="/posts/{-$category}"
  params={{ category: undefined }}
>
  All Posts
</Link>

// Navigate using parameter inheritance
<Link
  to="/posts/{-$category}"
  params={{}}
>
  Current Category
</Link>
// Navigate with optional parameter
<Link
  to="/posts/{-$category}"
  params={{ category: 'tech' }}
>
  Tech Posts
</Link>

// Navigate without optional parameter
<Link
  to="/posts/{-$category}"
  params={{ category: undefined }}
>
  All Posts
</Link>

// Navigate using parameter inheritance
<Link
  to="/posts/{-$category}"
  params={{}}
>
  Current Category
</Link>

Funktionsbasierte Parameteraktualisierungen

Funktionsbasierte Parameteraktualisierungen sind besonders nützlich bei optionalen Parametern.

tsx
// Remove a parameter using function syntax
<Link
  to="/posts/{-$category}"
  params={(prev) => ({ ...prev, category: undefined })}
>
  Clear Category
</Link>

// Update a parameter while keeping others
<Link
  to="/articles/{-$category}/{-$slug}"
  params={(prev) => ({ ...prev, category: 'news' })}
>
  News Articles
</Link>

// Conditionally set parameters
<Link
  to="/posts/{-$category}"
  params={(prev) => ({
    ...prev,
    category: someCondition ? 'tech' : undefined
  })}
>
  Conditional Category
</Link>
// Remove a parameter using function syntax
<Link
  to="/posts/{-$category}"
  params={(prev) => ({ ...prev, category: undefined })}
>
  Clear Category
</Link>

// Update a parameter while keeping others
<Link
  to="/articles/{-$category}/{-$slug}"
  params={(prev) => ({ ...prev, category: 'news' })}
>
  News Articles
</Link>

// Conditionally set parameters
<Link
  to="/posts/{-$category}"
  params={(prev) => ({
    ...prev,
    category: someCondition ? 'tech' : undefined
  })}
>
  Conditional Category
</Link>

Mehrere optionale Parameter

Wenn Sie mit mehreren optionalen Parametern arbeiten, können Sie mischen und anpassen, welche davon eingeschlossen werden sollen.

tsx
// Navigate with some optional parameters
<Link
  to="/posts/{-$category}/{-$slug}"
  params={{ category: 'tech', slug: undefined }}
>
  Tech Posts
</Link>

// Remove all optional parameters
<Link
  to="/posts/{-$category}/{-$slug}"
  params={{ category: undefined, slug: undefined }}
>
  All Posts
</Link>

// Set multiple parameters
<Link
  to="/posts/{-$category}/{-$slug}"
  params={{ category: 'tech', slug: 'react-tips' }}
>
  Specific Post
</Link>
// Navigate with some optional parameters
<Link
  to="/posts/{-$category}/{-$slug}"
  params={{ category: 'tech', slug: undefined }}
>
  Tech Posts
</Link>

// Remove all optional parameters
<Link
  to="/posts/{-$category}/{-$slug}"
  params={{ category: undefined, slug: undefined }}
>
  All Posts
</Link>

// Set multiple parameters
<Link
  to="/posts/{-$category}/{-$slug}"
  params={{ category: 'tech', slug: 'react-tips' }}
>
  Specific Post
</Link>

Gemischte erforderliche und optionale Parameter

Optionale Parameter funktionieren nahtlos mit erforderlichen Parametern.

tsx
// Required 'id', optional 'tab'
<Link
  to="/users/$id/{-$tab}"
  params={{ id: '123', tab: 'settings' }}
>
  User Settings
</Link>

// Remove optional parameter while keeping required
<Link
  to="/users/$id/{-$tab}"
  params={{ id: '123', tab: undefined }}
>
  User Profile
</Link>

// Use function style with mixed parameters
<Link
  to="/users/$id/{-$tab}"
  params={(prev) => ({ ...prev, tab: 'notifications' })}
>
  User Notifications
</Link>
// Required 'id', optional 'tab'
<Link
  to="/users/$id/{-$tab}"
  params={{ id: '123', tab: 'settings' }}
>
  User Settings
</Link>

// Remove optional parameter while keeping required
<Link
  to="/users/$id/{-$tab}"
  params={{ id: '123', tab: undefined }}
>
  User Profile
</Link>

// Use function style with mixed parameters
<Link
  to="/users/$id/{-$tab}"
  params={(prev) => ({ ...prev, tab: 'notifications' })}
>
  User Notifications
</Link>

Erweiterte Muster für optionale Parameter

Präfix- und Suffixparameter Optionale Parameter mit Präfix/Suffix funktionieren mit der Navigation.

tsx
// Navigate to file with optional name
<Link
  to="/files/prefix{-$name}.txt"
  params={{ name: 'document' }}
>
  Document File
</Link>

// Navigate to file without optional name
<Link
  to="/files/prefix{-$name}.txt"
  params={{ name: undefined }}
>
  Default File
</Link>
// Navigate to file with optional name
<Link
  to="/files/prefix{-$name}.txt"
  params={{ name: 'document' }}
>
  Document File
</Link>

// Navigate to file without optional name
<Link
  to="/files/prefix{-$name}.txt"
  params={{ name: undefined }}
>
  Default File
</Link>

Alle optionalen Parameter Routen, bei denen alle Parameter optional sind.

tsx
// Navigate to specific date
<Link
  to="/{-$year}/{-$month}/{-$day}"
  params={{ year: '2023', month: '12', day: '25' }}
>
  Christmas 2023
</Link>

// Navigate to partial date
<Link
  to="/{-$year}/{-$month}/{-$day}"
  params={{ year: '2023', month: '12', day: undefined }}
>
  December 2023
</Link>

// Navigate to root with all parameters removed
<Link
  to="/{-$year}/{-$month}/{-$day}"
  params={{ year: undefined, month: undefined, day: undefined }}
>
  Home
</Link>
// Navigate to specific date
<Link
  to="/{-$year}/{-$month}/{-$day}"
  params={{ year: '2023', month: '12', day: '25' }}
>
  Christmas 2023
</Link>

// Navigate to partial date
<Link
  to="/{-$year}/{-$month}/{-$day}"
  params={{ year: '2023', month: '12', day: undefined }}
>
  December 2023
</Link>

// Navigate to root with all parameters removed
<Link
  to="/{-$year}/{-$month}/{-$day}"
  params={{ year: undefined, month: undefined, day: undefined }}
>
  Home
</Link>

Optionale Parameter funktionieren hervorragend in Kombination mit Suchparametern.

tsx
// Combine optional path params with search params
<Link
  to="/posts/{-$category}"
  params={{ category: 'tech' }}
  search={{ page: 1, sort: 'newest' }}
>
  Tech Posts - Page 1
</Link>

// Remove path param but keep search params
<Link
  to="/posts/{-$category}"
  params={{ category: undefined }}
  search={(prev) => prev}
>
  All Posts - Same Filters
</Link>
// Combine optional path params with search params
<Link
  to="/posts/{-$category}"
  params={{ category: 'tech' }}
  search={{ page: 1, sort: 'newest' }}
>
  Tech Posts - Page 1
</Link>

// Remove path param but keep search params
<Link
  to="/posts/{-$category}"
  params={{ category: undefined }}
  search={(prev) => prev}
>
  All Posts - Same Filters
</Link>

Imperative Navigation mit optionalen Parametern

Alle gleichen Muster funktionieren mit imperativer Navigation.

tsx
function Component() {
  const navigate = useNavigate()

  const clearFilters = () => {
    navigate({
      to: '/posts/{-$category}/{-$tag}',
      params: { category: undefined, tag: undefined },
    })
  }

  const setCategory = (category: string) => {
    navigate({
      to: '/posts/{-$category}/{-$tag}',
      params: (prev) => ({ ...prev, category }),
    })
  }

  const applyFilters = (category?: string, tag?: string) => {
    navigate({
      to: '/posts/{-$category}/{-$tag}',
      params: { category, tag },
    })
  }
}
function Component() {
  const navigate = useNavigate()

  const clearFilters = () => {
    navigate({
      to: '/posts/{-$category}/{-$tag}',
      params: { category: undefined, tag: undefined },
    })
  }

  const setCategory = (category: string) => {
    navigate({
      to: '/posts/{-$category}/{-$tag}',
      params: (prev) => ({ ...prev, category }),
    })
  }

  const applyFilters = (category?: string, tag?: string) => {
    navigate({
      to: '/posts/{-$category}/{-$tag}',
      params: { category, tag },
    })
  }
}

active & inactive Props

Die Link-Komponente unterstützt zwei zusätzliche Props: activeProps und inactiveProps. Diese Props sind Funktionen, die zusätzliche Props für die aktiven und inaktiven Zustände des Links zurückgeben. Alle hier übergebenen Props außer Stilen und Klassen überschreiben die ursprünglichen Props, die an Link übergeben wurden. Alle übergebenen Stile oder Klassen werden zusammengeführt.

Hier ist ein Beispiel

tsx
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
    activeProps={{
      style: {
        fontWeight: 'bold',
      },
    }}
  >
    Section 1
  </Link>
)
const link = (
  <Link
    to="/blog/post/$postId"
    params={{
      postId: 'my-first-blog-post',
    }}
    activeProps={{
      style: {
        fontWeight: 'bold',
      },
    }}
  >
    Section 1
  </Link>
)

Das data-status Attribut

Zusätzlich zu den Props activeProps und inactiveProps fügt die Link-Komponente dem gerenderten Element ein data-status-Attribut hinzu, wenn es sich in einem aktiven Zustand befindet. Dieses Attribut ist active oder undefined, abhängig vom aktuellen Zustand des Links. Dies kann nützlich sein, wenn Sie Datenattribute zum Stylen Ihrer Links anstelle von Props bevorzugen.

Active Options

Die Link-Komponente verfügt über eine activeOptions-Eigenschaft, die einige Optionen zur Bestimmung bietet, ob ein Link aktiv ist oder nicht. Die folgende Schnittstelle beschreibt diese Optionen:

tsx
export interface ActiveOptions {
  // If true, the link will be active if the current route matches the `to` route path exactly (no children routes)
  // Defaults to `false`
  exact?: boolean
  // If true, the link will only be active if the current URL hash matches the `hash` prop
  // Defaults to `false`
  includeHash?: boolean // Defaults to false
  // If true, the link will only be active if the current URL search params inclusively match the `search` prop
  // Defaults to `true`
  includeSearch?: boolean
  // This modifies the `includeSearch` behavior.
  // If true,  properties in `search` that are explicitly `undefined` must NOT be present in the current URL search params for the link to be active.
  // defaults to `false`
  explicitUndefined?: boolean
}
export interface ActiveOptions {
  // If true, the link will be active if the current route matches the `to` route path exactly (no children routes)
  // Defaults to `false`
  exact?: boolean
  // If true, the link will only be active if the current URL hash matches the `hash` prop
  // Defaults to `false`
  includeHash?: boolean // Defaults to false
  // If true, the link will only be active if the current URL search params inclusively match the `search` prop
  // Defaults to `true`
  includeSearch?: boolean
  // This modifies the `includeSearch` behavior.
  // If true,  properties in `search` that are explicitly `undefined` must NOT be present in the current URL search params for the link to be active.
  // defaults to `false`
  explicitUndefined?: boolean
}

Standardmäßig wird geprüft, ob der resultierende Pfadname ein Präfix der aktuellen Route ist. Wenn Suchparameter angegeben sind, wird geprüft, ob diese die Parameter im aktuellen Speicherort *inklusiv* übereinstimmen. Hashes werden standardmäßig nicht geprüft.

Zum Beispiel, wenn Sie sich auf der Route /blog/post/my-first-blog-post befinden, sind die folgenden Links aktiv:

tsx
const link1 = (
  <Link to="/blog/post/$postId" params={{ postId: 'my-first-blog-post' }}>
    Blog Post
  </Link>
)
const link2 = <Link to="/blog/post">Blog Post</Link>
const link3 = <Link to="/blog">Blog Post</Link>
const link1 = (
  <Link to="/blog/post/$postId" params={{ postId: 'my-first-blog-post' }}>
    Blog Post
  </Link>
)
const link2 = <Link to="/blog/post">Blog Post</Link>
const link3 = <Link to="/blog">Blog Post</Link>

Die folgenden Links sind jedoch nicht aktiv:

tsx
const link4 = (
  <Link to="/blog/post/$postId" params={{ postId: 'my-second-blog-post' }}>
    Blog Post
  </Link>
)
const link4 = (
  <Link to="/blog/post/$postId" params={{ postId: 'my-second-blog-post' }}>
    Blog Post
  </Link>
)

Es ist üblich, dass einige Links nur aktiv sind, wenn sie eine exakte Übereinstimmung sind. Ein gutes Beispiel dafür wäre ein Link zur Startseite. In solchen Szenarien können Sie die Option exact: true übergeben.

tsx
const link = (
  <Link to="/" activeOptions={{ exact: true }}>
    Home
  </Link>
)
const link = (
  <Link to="/" activeOptions={{ exact: true }}>
    Home
  </Link>
)

Dadurch wird sichergestellt, dass der Link nicht aktiv ist, wenn Sie sich auf einer Kindroute befinden.

Einige weitere Optionen, die Sie kennen sollten:

  • Wenn Sie den Hash in Ihre Übereinstimmung einbeziehen möchten, können Sie die Option includeHash: true übergeben.
  • Wenn Sie die Suchparameter nicht in Ihre Übereinstimmung einbeziehen möchten, können Sie die Option includeSearch: false übergeben.

Übergabe von isActive an Kinder

Die Link-Komponente akzeptiert eine Funktion für ihre Kinder, die es Ihnen ermöglicht, ihre isActive-Eigenschaft an Kinder weiterzugeben. Zum Beispiel könnten Sie eine Kindkomponente basierend darauf stylen, ob der Elternlink aktiv ist.

tsx
const link = (
  <Link to="/blog/post">
    {({ isActive }) => {
      return (
        <>
          <span>My Blog Post</span>
          <icon className={isActive ? 'active' : 'inactive'} />
        </>
      )
    }}
  </Link>
)
const link = (
  <Link to="/blog/post">
    {({ isActive }) => {
      return (
        <>
          <span>My Blog Post</span>
          <icon className={isActive ? 'active' : 'inactive'} />
        </>
      )
    }}
  </Link>
)

Die Link-Komponente unterstützt das automatische Vorabladen von Routen bei Absicht (derzeit Hover oder Touchstart). Dies kann als Standard in den Router-Optionen konfiguriert werden (worüber wir bald mehr sprechen werden) oder durch Übergabe eines preload='intent'-Props an die Link-Komponente. Hier ist ein Beispiel:

tsx
const link = (
  <Link to="/blog/post/$postId" preload="intent">
    Blog Post
  </Link>
)
const link = (
  <Link to="/blog/post/$postId" preload="intent">
    Blog Post
  </Link>
)

Mit aktivierter Vorabladung und relativ schnellen asynchronen Routenabhängigkeiten (falls vorhanden) kann dieser einfache Trick die wahrgenommene Leistung Ihrer Anwendung mit sehr wenig Aufwand steigern.

Was noch besser ist: Durch die Verwendung einer Cache-First-Bibliothek wie @tanstack/query bleiben vorab geladene Routen erhalten und sind für eine Stale-While-Revalidate-Erfahrung bereit, falls der Benutzer später zu dieser Route navigiert.

Zusammen mit der Vorabladung gibt es eine konfigurierbare Verzögerung, die bestimmt, wie lange ein Benutzer mit der Maus über einen Link fahren muss, um die vorab ladende Vorabladung auszulösen. Die Standardverzögerung beträgt 50 Millisekunden, aber Sie können dies ändern, indem Sie einen preloadDelay-Prop an die Link-Komponente übergeben, mit der Anzahl der Millisekunden, die Sie warten möchten.

tsx
const link = (
  <Link to="/blog/post/$postId" preload="intent" preloadDelay={100}>
    Blog Post
  </Link>
)
const link = (
  <Link to="/blog/post/$postId" preload="intent" preloadDelay={100}>
    Blog Post
  </Link>
)

useNavigate

⚠️ Aufgrund der integrierten Funktionalitäten der Link-Komponente bezüglich href, Cmd/Strg + Klickbarkeit und aktiven/inaktiven Funktionen wird empfohlen, die Link-Komponente anstelle von useNavigate für alles zu verwenden, womit der Benutzer interagieren kann (z. B. Links, Schaltflächen). Es gibt jedoch einige Fälle, in denen useNavigate zur Behandlung von Nebeneffekt-Navigationen (z. B. eine erfolgreiche asynchrone Aktion, die zu einer Navigation führt) erforderlich ist.

Der Hook useNavigate gibt eine navigate-Funktion zurück, die aufgerufen werden kann, um imperativ zu navigieren. Es ist eine großartige Möglichkeit, von einem Nebeneffekt zu einer Route zu navigieren (z. B. eine erfolgreiche asynchrone Aktion). Hier ist ein Beispiel:

tsx
function Component() {
  const navigate = useNavigate({ from: '/posts/$postId' })

  const handleSubmit = async (e: FrameworkFormEvent) => {
    e.preventDefault()

    const response = await fetch('/posts', {
      method: 'POST',
      body: JSON.stringify({ title: 'My First Post' }),
    })

    const { id: postId } = await response.json()

    if (response.ok) {
      navigate({ to: '/posts/$postId', params: { postId } })
    }
  }
}
function Component() {
  const navigate = useNavigate({ from: '/posts/$postId' })

  const handleSubmit = async (e: FrameworkFormEvent) => {
    e.preventDefault()

    const response = await fetch('/posts', {
      method: 'POST',
      body: JSON.stringify({ title: 'My First Post' }),
    })

    const { id: postId } = await response.json()

    if (response.ok) {
      navigate({ to: '/posts/$postId', params: { postId } })
    }
  }
}

🧠 Wie oben gezeigt, können Sie die Option from übergeben, um die Route anzugeben, von der aus in dem Hook-Aufruf navigiert werden soll. Während dies auch möglich ist, wenn Sie es jedes Mal, wenn Sie es aufrufen, an die resultierende navigate-Funktion übergeben, wird empfohlen, es hier zu übergeben, um potenzielle Fehler zu reduzieren und auch weniger tippen zu müssen!

Die von useNavigate zurückgegebene Funktion navigate akzeptiert die Schnittstelle NavigateOptions.

Gelegentlich müssen Sie möglicherweise sofort navigieren, wenn eine Komponente gemountet wird. Ihr erster Instinkt könnte sein, nach useNavigate und einem sofortigen Nebeneffekt (z. B. useEffect) zu greifen, aber das ist unnötig. Stattdessen können Sie die Navigate-Komponente rendern, um das gleiche Ergebnis zu erzielen.

tsx
function Component() {
  return <Navigate to="/posts/$postId" params={{ postId: 'my-first-post' }} />
}
function Component() {
  return <Navigate to="/posts/$postId" params={{ postId: 'my-first-post' }} />
}

Betrachten Sie die Navigate-Komponente als eine Möglichkeit, sofort zu einer Route zu navigieren, wenn eine Komponente gemountet wird. Sie ist eine großartige Möglichkeit, clientseitige Weiterleitungen zu behandeln. Sie ist *definitiv nicht* ein Ersatz für die verantwortungsvolle Behandlung von serverseitig bewussten Weiterleitungen auf dem Server.

router.navigate

Die Methode router.navigate ist identisch mit der Funktion navigate, die von useNavigate zurückgegeben wird, und akzeptiert dieselbe Schnittstelle NavigateOptions. Im Gegensatz zum Hook useNavigate ist sie überall dort verfügbar, wo Ihre router-Instanz verfügbar ist, und ist somit eine großartige Möglichkeit, imperativ von überall in Ihrer Anwendung zu navigieren, auch außerhalb Ihres Frameworks.

useMatchRoute und <MatchRoute>

Der Hook useMatchRoute und die Komponente <MatchRoute> sind dasselbe, aber der Hook ist etwas flexibler. Beide akzeptieren die Standard-Navigationsschnittstelle ToOptions entweder als Optionen oder als Props und geben true/false zurück, wenn diese Route aktuell übereinstimmt. Sie hat auch eine praktische pending-Option, die true zurückgibt, wenn die Route gerade ausgeführt wird (z. B. wenn eine Route gerade zu dieser Route wechselt). Dies kann äußerst nützlich sein, um optimistische Benutzeroberflächen rund um die Navigation des Benutzers anzuzeigen.

tsx
function Component() {
  return (
    <div>
      <Link to="/users">
        Users
        <MatchRoute to="/users" pending>
          <Spinner />
        </MatchRoute>
      </Link>
    </div>
  )
}
function Component() {
  return (
    <div>
      <Link to="/users">
        Users
        <MatchRoute to="/users" pending>
          <Spinner />
        </MatchRoute>
      </Link>
    </div>
  )
}

Die Komponentenversion <MatchRoute> kann auch mit einer Funktion als Kinder verwendet werden, um etwas zu rendern, wenn die Route übereinstimmt.

tsx
function Component() {
  return (
    <div>
      <Link to="/users">
        Users
        <MatchRoute to="/users" pending>
          {(match) => {
            return <Spinner show={match} />
          }}
        </MatchRoute>
      </Link>
    </div>
  )
}
function Component() {
  return (
    <div>
      <Link to="/users">
        Users
        <MatchRoute to="/users" pending>
          {(match) => {
            return <Spinner show={match} />
          }}
        </MatchRoute>
      </Link>
    </div>
  )
}

Die Hook-Version useMatchRoute gibt eine Funktion zurück, die programmatisch aufgerufen werden kann, um zu prüfen, ob eine Route übereinstimmt.

tsx
function Component() {
  const matchRoute = useMatchRoute()

  useEffect(() => {
    if (matchRoute({ to: '/users', pending: true })) {
      console.info('The /users route is matched and pending')
    }
  })

  return (
    <div>
      <Link to="/users">Users</Link>
    </div>
  )
}
function Component() {
  const matchRoute = useMatchRoute()

  useEffect(() => {
    if (matchRoute({ to: '/users', pending: true })) {
      console.info('The /users route is matched and pending')
    }
  })

  return (
    <div>
      <Link to="/users">Users</Link>
    </div>
  )
}

Puh! Das ist eine Menge Navigation! Hoffentlich fühlen Sie sich jetzt ziemlich gut damit, sich in Ihrer Anwendung zurechtzufinden. Lassen Sie uns weitermachen!

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.