Menu

useRouter

Wenn Sie innerhalb einer Funktionskomponente in Ihrer App auf das router-Objekt zugreifen möchten, können Sie den useRouter Hook verwenden. Hier ist ein Beispiel:

import { useRouter } from 'next/router'
 
function ActiveLink({ children, href }) {
  const router = useRouter()
  const style = {
    marginRight: 10,
    color: router.asPath === href ? 'red' : 'black',
  }
 
  const handleClick = (e) => {
    e.preventDefault()
    router.push(href)
  }
 
  return (
    <a href={href} onClick={handleClick} style={style}>
      {children}
    </a>
  )
}
 
export default ActiveLink

useRouter ist ein React Hook, was bedeutet, dass er nicht mit Klassen verwendet werden kann. Sie können entweder withRouter verwenden oder Ihre Klasse in einer Funktionskomponente einwickeln.

router-Objekt

Dies ist die Definition des router-Objekts, das sowohl von useRouter als auch von withRouter zurückgegeben wird:

  • pathname: String - Der Pfad für die aktuelle Routendatei nach /pages. Daher sind basePath, locale und der abschließende Schrägstrich (trailingSlash: true) nicht enthalten.
  • query: Object - Die als Objekt geparste Abfragezeichenfolge, einschließlich dynamischer Routenparameter. Bei Vorrendering wird es ein leeres Objekt sein, wenn die Seite keine serverseitige Rendering verwendet. Standard ist {}
  • asPath: String - Der Pfad, wie er im Browser angezeigt wird, einschließlich Suchparametern und unter Berücksichtigung der trailingSlash-Konfiguration. basePath und locale sind nicht enthalten.
  • isFallback: boolean - Ob die aktuelle Seite sich im Fallback-Modus befindet.
  • basePath: String - Der aktive basePath (wenn aktiviert).
  • locale: String - Das aktive Gebietsschema (wenn aktiviert).
  • locales: String[] - Alle unterstützten Gebietsschemas (wenn aktiviert).
  • defaultLocale: String - Das aktuelle Standard-Gebietsschema (wenn aktiviert).
  • domainLocales: Array<{domain, defaultLocale, locales}> - Alle konfigurierten Domain-Gebietsschemas.
  • isReady: boolean - Ob die Router-Felder clientseitig aktualisiert und zur Verwendung bereit sind. Sollte nur innerhalb von useEffect-Methoden verwendet werden und nicht für bedingtes Rendering auf dem Server. Siehe zugehörige Dokumentation für den Anwendungsfall mit automatisch statisch optimierten Seiten
  • isPreview: boolean - Ob die Anwendung sich derzeit im Vorschaumodus befindet.

Die Verwendung des asPath-Feldes kann zu einer Diskrepanz zwischen Client und Server führen, wenn die Seite mit serverseitigem Rendering oder automatischer statischer Optimierung gerendert wird. Vermeiden Sie die Verwendung von asPath, bis das isReady-Feld true ist.

Die folgenden Methoden sind im router enthalten:

router.push

Behandelt clientseitige Übergänge, diese Methode ist nützlich für Fälle, in denen next/link nicht ausreicht.

router.push(url, as, options)
  • url: UrlObject | String - Die URL, zu der navigiert werden soll (siehe Node.JS URL-Modul-Dokumentation für UrlObject-Eigenschaften).
  • as: UrlObject | String - Optionaler Decorator für den Pfad, der in der Browser-URL-Leiste angezeigt wird. Vor Next.js 9.5.3 wurde dies für dynamische Routen verwendet.
  • options - Optionales Objekt mit folgenden Konfigurationsoptionen:
    • scroll - Optionaler boolescher Wert, steuert das Scrollen zum Seitenanfang nach der Navigation. Standard ist true
    • shallow: Aktualisiert den Pfad der aktuellen Seite ohne erneutes Ausführen von getStaticProps, getServerSideProps oder getInitialProps. Standard ist false
    • locale - Optionaler String, gibt das Gebietsschema der neuen Seite an

Für externe URLs müssen Sie router.push nicht verwenden. window.location ist dafür besser geeignet.

Navigieren zu pages/about.js, welches eine vordefinierte Route ist:

import { useRouter } from 'next/router'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.push('/about')}>
      Klick mich
    </button>
  )
}

Navigieren zu pages/post/[pid].js, welches eine dynamische Route ist:

import { useRouter } from 'next/router'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.push('/post/abc')}>
      Klick mich
    </button>
  )
}

Umleitung des Benutzers zu pages/login.js, nützlich für Seiten hinter Authentifizierung:

import { useEffect } from 'react'
import { useRouter } from 'next/router'
 
// Hier würden Sie den Benutzer abrufen und zurückgeben
const useUser = () => ({ user: null, loading: false })
 
export default function Page() {
  const { user, loading } = useUser()
  const router = useRouter()
 
  useEffect(() => {
    if (!(user || loading)) {
      router.push('/login')
    }
  }, [user, loading])
 
  return <p>Umleitung...</p>
}

Zurücksetzen des Status nach der Navigation

Bei der Navigation zur gleichen Seite in Next.js wird der Status der Seite standardmäßig nicht zurückgesetzt, da React nicht unmounted wird, wenn sich die übergeordnete Komponente nicht geändert hat.

pages/[slug].js
import Link from 'next/link'
import { useState } from 'react'
import { useRouter } from 'next/router'
 
export default function Page(props) {
  const router = useRouter()
  const [count, setCount] = useState(0)
  return (
    <div>
      <h1>Seite: {router.query.slug}</h1>
      <p>Zähler: {count}</p>
      <button onClick={() => setCount(count + 1)}>Zähler erhöhen</button>
      <Link href="/one">one</Link> <Link href="/two">two</Link>
    </div>
  )
}

Im obigen Beispiel führt die Navigation zwischen /one und /two nicht zum Zurücksetzen des Zählers. Das useState wird zwischen den Renderings beibehalten, da die React-Komponente der obersten Ebene, Page, gleich bleibt.

Wenn Sie dieses Verhalten nicht wünschen, haben Sie ein paar Optionen:

  • Stellen Sie sicher, dass jeder Status manuell mit useEffect aktualisiert wird. Im obigen Beispiel könnte das so aussehen:

    useEffect(() => {
      setCount(0)
    }, [router.query.slug])
  • Verwenden Sie einen React key, um React anzuweisen, die Komponente neu zu mounten. Um dies für alle Seiten zu tun, können Sie eine benutzerdefinierte App verwenden:

    pages/_app.js
    import { useRouter } from 'next/router'
     
    export default function MyApp({ Component, pageProps }) {
      const router = useRouter()
      return <Component key={router.asPath} {...pageProps} />
    }

Mit URL-Objekt

Sie können ein URL-Objekt genauso verwenden wie für next/link. Funktioniert sowohl für url als auch für as-Parameter:

import { useRouter } from 'next/router'
 
export default function ReadMore({ post }) {
  const router = useRouter()
 
  return (
    <button
      type="button"
      onClick={() => {
        router.push({
          pathname: '/post/[pid]',
          query: { pid: post.id },
        })
      }}
    >
      Hier klicken, um mehr zu lesen
    </button>
  )
}

router.replace

Ähnlich dem replace-Prop in next/link verhindert router.replace das Hinzufügen eines neuen URL-Eintrags in den history-Stack.

router.replace(url, as, options)
  • Die API für router.replace ist genau dieselbe wie die API für router.push.

Werfen Sie einen Blick auf das folgende Beispiel:

import { useRouter } from 'next/router'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.replace('/home')}>
      Klick mich
    </button>
  )
}

router.prefetch

Seiten vorabladen für schnellere clientseitige Übergänge. Diese Methode ist nur nützlich für Navigationen ohne next/link, da next/link automatisch das Vorabladen von Seiten übernimmt.

Dies ist eine Funktion, die nur in der Produktionsumgebung verfügbar ist. Next.js lädt Seiten in der Entwicklungsumgebung nicht vorab.

router.prefetch(url, as, options)
  • url - Die URL zum Vorabladen, einschließlich expliziter Routen (z.B. /dashboard) und dynamischer Routen (z.B. /product/[id])
  • as - Optionaler Decorator für url. Vor Next.js 9.5.3 wurde dies zum Vorabladen dynamischer Routen verwendet.
  • options - Optionales Objekt mit den folgenden erlaubten Feldern:
    • locale - erlaubt die Angabe eines anderen Gebietsschemas als des aktiven. Wenn false, muss url das Gebietsschema enthalten, da das aktive Gebietsschema nicht verwendet wird.

Nehmen wir an, Sie haben eine Anmeldeseite und leiten den Benutzer nach der Anmeldung zum Dashboard weiter. In diesem Fall können wir das Dashboard vorabladen, um einen schnelleren Übergang zu ermöglichen, wie im folgenden Beispiel:

import { useCallback, useEffect } from 'react'
import { useRouter } from 'next/router'
 
export default function Login() {
  const router = useRouter()
  const handleSubmit = useCallback((e) => {
    e.preventDefault()
 
    fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        /* Formulardaten */
      }),
    }).then((res) => {
      // Schnelle clientseitige Weiterleitung zur bereits vorgeladenen Dashboard-Seite
      if (res.ok) router.push('/dashboard')
    })
  }, [])
 
  useEffect(() => {
    // Dashboard-Seite vorabladen
    router.prefetch('/dashboard')
  }, [router])
 
  return (
    <form onSubmit={handleSubmit}>
      {/* Formularfelder */}
      <button type="submit">Anmelden</button>
    </form>
  )
}

router.beforePopState

In einigen Fällen (zum Beispiel bei Verwendung eines Custom Servers) möchten Sie möglicherweise den popstate-Event abhören und etwas tun, bevor der Router darauf reagiert.

router.beforePopState(cb)
  • cb - Die Funktion, die bei eingehenden popstate-Events ausgeführt wird. Die Funktion erhält den Zustand des Events als Objekt mit folgenden Eigenschaften:
    • url: String - die Route für den neuen Zustand. Dies ist normalerweise der Name einer Seite
    • as: String - die URL, die im Browser angezeigt wird
    • options: Object - Zusätzliche Optionen, die von router.push gesendet werden

Wenn cb false zurückgibt, behandelt der Next.js-Router popstate nicht, und Sie sind in diesem Fall für die Behandlung verantwortlich. Weitere Informationen finden Sie unter Deaktivieren des Dateisystem-Routings.

Sie könnten beforePopState verwenden, um die Anfrage zu manipulieren oder eine SSR-Aktualisierung zu erzwingen, wie im folgenden Beispiel:

import { useEffect } from 'react'
import { useRouter } from 'next/router'
 
export default function Page() {
  const router = useRouter()
 
  useEffect(() => {
    router.beforePopState(({ url, as, options }) => {
      // Ich möchte nur diese beiden Routen zulassen!
      if (as !== '/' && as !== '/other') {
        // Schlecht geroutete Routen als 404 rendern lassen.
        window.location.href = as
        return false
      }
 
      return true
    })
  }, [router])
 
  return <p>Willkommen auf der Seite</p>
}

router.back

Zur vorherigen Seite in der Verlaufschronik zurückkehren. Entspricht dem Klicken auf die Zurück-Taste des Browsers. Führt window.history.back() aus.

import { useRouter } from 'next/router'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.back()}>
      Hier klicken, um zurückzugehen
    </button>
  )
}

router.reload

Die aktuelle URL neu laden. Entspricht dem Klicken auf die Aktualisieren-Taste des Browsers. Führt window.location.reload() aus.

import { useRouter } from 'next/router'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.reload()}>
      Hier klicken, um neu zu laden
    </button>
  )
}

router.events

Sie können verschiedene Ereignisse im Next.js-Router verfolgen. Hier ist eine Liste der unterstützten Ereignisse:

  • routeChangeStart(url, { shallow }) - Wird ausgelöst, wenn eine Route zu wechseln beginnt
  • routeChangeComplete(url, { shallow }) - Wird ausgelöst, wenn eine Route vollständig gewechselt hat
  • routeChangeError(err, url, { shallow }) - Wird ausgelöst, wenn ein Fehler beim Routenwechsel auftritt oder ein Routenladen abgebrochen wird
    • err.cancelled - Zeigt an, ob die Navigation abgebrochen wurde
  • beforeHistoryChange(url, { shallow }) - Wird ausgelöst, bevor sich der Browserverlauf ändert
  • hashChangeStart(url, { shallow }) - Wird ausgelöst, wenn sich der Hash ändern wird, aber nicht die Seite
  • hashChangeComplete(url, { shallow }) - Wird ausgelöst, wenn sich der Hash geändert hat, aber nicht die Seite

Hinweis: Hier ist url die im Browser angezeigte URL, einschließlich des basePath.

Um beispielsweise das Router-Ereignis routeChangeStart zu verfolgen, öffnen oder erstellen Sie pages/_app.js und abonnieren Sie das Ereignis wie folgt:

import { useEffect } from 'react'
import { useRouter } from 'next/router'
 
export default function MyApp({ Component, pageProps }) {
  const router = useRouter()
 
  useEffect(() => {
    const handleRouteChange = (url, { shallow }) => {
      console.log(
        `App wechselt zu ${url} ${
          shallow ? 'mit' : 'ohne'
        } flachem Routing`
      )
    }
 
    router.events.on('routeChangeStart', handleRouteChange)
 
    // Wenn die Komponente unmontiert wird, vom Ereignis mit der `off`-Methode abmelden:
    return () => {
      router.events.off('routeChangeStart', handleRouteChange)
    }
  }, [router])
 
  return <Component {...pageProps} />
}

Wir verwenden eine Benutzerdefinierte App (pages/_app.js), um das Ereignis zu abonnieren, da sie bei Seitennavigationen nicht unmontiert wird, aber Sie können Router-Ereignisse in jeder Komponente Ihrer Anwendung abonnieren.

Router-Ereignisse sollten registriert werden, wenn eine Komponente montiert wird (useEffect oder componentDidMount / componentWillUnmount) oder imperativ, wenn ein Ereignis auftritt.

Wenn ein Routenladen abgebrochen wird (z.B. durch schnelles Klicken auf zwei Links), wird routeChangeError ausgelöst. Und der übergebene err wird eine cancelled-Eigenschaft besitzen, die auf true gesetzt ist, wie im folgenden Beispiel:

import { useEffect } from 'react'
import { useRouter } from 'next/router'
 
export default function MyApp({ Component, pageProps }) {
  const router = useRouter()
 
  useEffect(() => {
    const handleRouteChangeError = (err, url) => {
      if (err.cancelled) {
        console.log(`Route zu ${url} wurde abgebrochen!`)
      }
    }
 
    router.events.on('routeChangeError', handleRouteChangeError)
 
    // Wenn die Komponente unmontiert wird, vom Ereignis mit der `off`-Methode abmelden:
    return () => {
      router.events.off('routeChangeError', handleRouteChangeError)
    }
  }, [router])
 
  return <Component {...pageProps} />
}

Der next/compat/router Export

Dies ist derselbe useRouter-Hook, kann aber sowohl im app- als auch im pages-Verzeichnis verwendet werden.

Er unterscheidet sich von next/router dadurch, dass er keinen Fehler auslöst, wenn der Pages-Router nicht montiert ist, und stattdessen einen Rückgabetyp von NextRouter | null hat. Dies ermöglicht Entwicklern, Komponenten so zu konvertieren, dass sie sowohl im app- als auch im pages-Verzeichnis während des Übergangs zum app-Router laufen.

Eine Komponente, die vorher so aussah:

import { useRouter } from 'next/router'
const MyComponent = () => {
  const { isReady, query } = useRouter()
  // ...
}

Wird einen Fehler verursachen, wenn er zu next/compat/router konvertiert wird, da null nicht destrukturiert werden kann. Stattdessen können Entwickler neue Hooks nutzen:

import { useEffect } from 'react'
import { useRouter } from 'next/compat/router'
import { useSearchParams } from 'next/navigation'
const MyComponent = () => {
  const router = useRouter() // kann null oder eine NextRouter-Instanz sein
  const searchParams = useSearchParams()
  useEffect(() => {
    if (router && !router.isReady) {
      return
    }
    // In `app/` sind searchParams sofort mit den Werten bereit, in
    // `pages/` sind sie nach Bereitschaft des Routers verfügbar.
    const search = searchParams.get('search')
    // ...
  }, [router, searchParams])
  // ...
}

Diese Komponente funktioniert jetzt in beiden Verzeichnissen pages und app. Wenn die Komponente nicht mehr in pages verwendet wird, können Sie die Referenzen zum Compat-Router entfernen:

import { useSearchParams } from 'next/navigation'
const MyComponent = () => {
  const searchParams = useSearchParams()
  // Da diese Komponente nur in `app/` verwendet wird, kann der Compat-Router entfernt werden.
  const search = searchParams.get('search')
  // ...
}

Verwendung von useRouter außerhalb des Next.js-Kontexts in pages

Ein weiterer spezifischer Anwendungsfall ist die Rendering von Komponenten außerhalb des Next.js-Anwendungskontexts, z.B. innerhalb von getServerSideProps im pages-Verzeichnis. In diesem Fall kann der Compat-Router verwendet werden, um Fehler zu vermeiden:

import { renderToString } from 'react-dom/server'
import { useRouter } from 'next/compat/router'
const MyComponent = () => {
  const router = useRouter() // kann null oder eine NextRouter-Instanz sein
  // ...
}
export async function getServerSideProps() {
  const renderedComponent = renderToString(<MyComponent />)
  return {
    props: {
      renderedComponent,
    },
  }
}

Mögliche ESLint-Fehler

Bestimmte Methoden des router-Objekts geben ein Promise zurück. Wenn Sie die ESLint-Regel no-floating-promises aktiviert haben, erwägen Sie, sie entweder global oder für die betroffene Zeile zu deaktivieren.

Wenn Ihre Anwendung diese Regel benötigt, sollten Sie entweder das Promise voiden – oder eine async-Funktion verwenden, das Promise awaiten und dann den Funktionsaufruf voiden. Dies gilt nicht, wenn die Methode aus einem onClick-Handler heraus aufgerufen wird.

Die betroffenen Methoden sind:

  • router.push
  • router.replace
  • router.prefetch

Mögliche Lösungen

import { useEffect } from 'react'
import { useRouter } from 'next/router'
 
// Hier würden Sie den Benutzer abrufen und zurückgeben
const useUser = () => ({ user: null, loading: false })
 
export default function Page() {
  const { user, loading } = useUser()
  const router = useRouter()
 
  useEffect(() => {
    // Deaktivieren Sie die Lint-Prüfung in der nächsten Zeile - Dies ist die sauberste Lösung
    // eslint-disable-next-line no-floating-promises
    router.push('/login')
 
    // Void das von router.push zurückgegebene Promise
    if (!(user || loading)) {
      void router.push('/login')
    }
    // oder verwenden Sie eine async-Funktion, warten Sie das Promise ab und voidn dann den Funktionsaufruf
    async function handleRouteChange() {
      if (!(user || loading)) {
        await router.push('/login')
      }
    }
    void handleRouteChange()
  }, [user, loading])
 
  return <p>Umleitung...</p>
}

withRouter

Wenn useRouter nicht optimal für Sie ist, kann withRouter das gleiche router-Objekt zu jeder Komponente hinzufügen.

Verwendung

import { withRouter } from 'next/router'
 
function Page({ router }) {
  return <p>{router.pathname}</p>
}
 
export default withRouter(Page)

TypeScript

Um Klassenkomponenten mit withRouter zu verwenden, muss die Komponente eine Router-Prop akzeptieren:

import React from 'react'
import { withRouter, NextRouter } from 'next/router'
 
interface WithRouterProps {
  router: NextRouter
}
 
interface MyComponentProps extends WithRouterProps {}
 
class MyComponent extends React.Component<MyComponentProps> {
  render() {
    return <p>{this.props.router.pathname}</p>
  }
}
 
export default withRouter(MyComponent)