useRouter
Wenn Sie auf das router
-Objekt in einer beliebigen Funktionskomponente Ihrer Anwendung zugreifen möchten, können Sie den useRouter
-Hook verwenden. Sehen Sie sich das folgende Beispiel an:
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 eine Funktionskomponente einbinden.
router
-Objekt
Folgend ist die Definition des router
-Objekts, das sowohl von useRouter
als auch von withRouter
zurückgegeben wird:
pathname
:String
- Der Pfad der aktuellen Routendatei nach/pages
. Daher sindbasePath
,locale
und nachgestellter Schrägstrich (trailingSlash: true
) nicht enthalten.query
:Object
- Die Query-String als geparstes Objekt, einschließlich dynamischer Routen-Parameter. Während des Prerenderings ist es ein leeres Objekt, wenn die Seite kein Server-seitiges Rendering (SSR) verwendet. Standardwert:{}
asPath
:String
- Der im Browser angezeigte Pfad inklusive Suchparametern und unter Berücksichtigung dertrailingSlash
-Konfiguration.basePath
undlocale
sind nicht enthalten.isFallback
:boolean
- Gibt an, ob sich die aktuelle Seite im Fallback-Modus befindet.basePath
:String
- Der aktive basePath (falls aktiviert).locale
:String
- Die aktive Locale (falls aktiviert).locales
:String[]
- Alle unterstützten Locales (falls aktiviert).defaultLocale
:String
- Die aktuelle Standard-Locale (falls aktiviert).domainLocales
:Array<{domain, defaultLocale, locales}>
- Alle konfigurierten Domain-Locales.isReady
:boolean
- Gibt an, ob die Router-Felder clientseitig aktualisiert und einsatzbereit sind. Sollte nur innerhalb vonuseEffect
-Methoden verwendet werden und nicht für bedingtes Rendering auf dem Server. Siehe verwandte Dokumentation für den Anwendungsfall mit automatisch statisch optimierten SeitenisPreview
:boolean
- Gibt an, ob sich die Anwendung im Preview-Modus befindet.
Die Verwendung des
asPath
-Felds kann zu einer Diskrepanz zwischen Client und Server führen, wenn die Seite mit Server-seitigem Rendering oder automatischer statischer Optimierung gerendert wird. Vermeiden Sie die Verwendung vonasPath
, bis dasisReady
-Feldtrue
ist.
Folgende Methoden sind im router
-Objekt enthalten:
router.push
Handhabt clientseitige Übergänge. Diese Methode ist nützlich, wenn 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ürUrlObject
-Eigenschaften).as
:UrlObject | String
- Optionaler Dekorator 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
- Optionales Boolean, steuert das Scrollen zum Seitenanfang nach der Navigation. Standardwert:true
shallow
: Aktualisiert den Pfad der aktuellen Seite ohne erneutes Ausführen vongetStaticProps
,getServerSideProps
odergetInitialProps
. Standardwert:false
locale
- Optionaler String, gibt die Locale der neuen Seite an
Für externe URLs müssen Sie
router.push
nicht verwenden. window.location ist dafür besser geeignet.
Navigation zu pages/about.js
, einer vordefinierten Route:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/about')}>
Klick mich
</button>
)
}
Navigation zu pages/post/[pid].js
, einer dynamischen Route:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/post/abc')}>
Klick mich
</button>
)
}
Weiterleitung des Benutzers zu pages/login.js
, nützlich für Seiten hinter einer Authentifizierung:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Hier würden Sie den Benutzer abfragen 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>Weiterleiten...</p>
}
Zurücksetzen des Zustands nach der Navigation
Bei der Navigation zur gleichen Seite in Next.js wird der Seitenzustand standardmäßig nicht zurückgesetzt, da React die Komponente nicht aushängt, es sei denn, die übergeordnete Komponente hat sich geändert.
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">Eins</Link> <Link href="/two">Zwei</Link>
</div>
)
}
Im obigen Beispiel wird beim Navigieren zwischen /one
und /two
der Zähler nicht zurückgesetzt. Der useState
-Wert bleibt zwischen den Renderings erhalten, weil die übergeordnete React-Komponente Page
dieselbe ist.
Wenn Sie dieses Verhalten nicht wünschen, haben Sie mehrere Möglichkeiten:
-
Manuelles Aktualisieren jedes Zustands mit
useEffect
. Im obigen Beispiel könnte das so aussehen:useEffect(() => { setCount(0) }, [router.query.slug])
-
Verwenden Sie einen React
key
, um React mitzuteilen, dass die Komponente neu eingehängt werden soll. Für alle Seiten 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 auf die gleiche Weise verwenden wie für next/link
. Funktioniert sowohl für den url
- als auch den 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 wie die replace
-Prop in next/link
verhindert router.replace
, dass ein neuer URL-Eintrag in den history
-Stack hinzugefügt wird.
router.replace(url, as, options)
- Die API für
router.replace
ist identisch mit der API fürrouter.push
.
Sehen Sie sich das folgende Beispiel an:
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 vorab laden für schnellere clientseitige Übergänge. Diese Methode ist nur für Navigationen ohne next/link
nützlich, da next/link
das Vorabladen von Seiten automatisch übernimmt.
Dies ist eine Produktionsfunktion. Next.js lädt Seiten im Entwicklungsmodus nicht vorab.
router.prefetch(url, as, options)
url
- Die vorabzuladende URL, einschließlich expliziter Routen (z.B./dashboard
) und dynamischer Routen (z.B./product/[id]
)as
- Optionaler Dekorator fürurl
. Vor Next.js 9.5.3 wurde dies zum Vorabladen dynamischer Routen verwendet.options
- Optionales Objekt mit folgenden erlaubten Feldern:locale
- Ermöglicht die Angabe einer anderen Locale als der aktiven. Wennfalse
, mussurl
die Locale enthalten, da die aktive Locale nicht verwendet wird.
Angenommen, Sie haben eine Login-Seite, und nach dem Login leiten Sie den Benutzer zum Dashboard weiter. Für diesen Fall können wir das Dashboard vorab laden, 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) => {
// Schneller clientseitiger Übergang zur bereits vorab geladenen Dashboard-Seite
if (res.ok) router.push('/dashboard')
})
}, [])
useEffect(() => {
// Dashboard-Seite vorab laden
router.prefetch('/dashboard')
}, [router])
return (
<form onSubmit={handleSubmit}>
{/* Formularfelder */}
<button type="submit">Login</button>
</form>
)
}
router.beforePopState
In einigen Fällen (z.B. bei Verwendung eines Custom Servers) möchten Sie möglicherweise auf popstate hören und etwas tun, bevor der Router darauf reagiert.
router.beforePopState(cb)
cb
- Die Funktion, die bei eingehendenpopstate
-Ereignissen ausgeführt wird. Die Funktion erhält den Zustand des Ereignisses als Objekt mit folgenden Props:url
:String
- die Route für den neuen Zustand. Dies ist normalerweise der Name einerpage
as
:String
- die URL, die im Browser angezeigt wirdoptions
:Object
- Zusätzliche Optionen, die von router.push gesendet werden
Wenn cb
false
zurückgibt, wird der Next.js-Router popstate
nicht behandeln, und Sie sind dafür verantwortlich. Siehe Deaktivierung des Dateisystem-Routings.
Sie könnten beforePopState
verwenden, um die Anfrage zu manipulieren oder ein SSR-Refresh 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 erlauben!
if (as !== '/' && as !== '/other') {
// SSR soll ungültige Routen als 404 rendern
window.location.href = as
return false
}
return true
})
}, [router])
return <p>Willkommen auf der Seite</p>
}
router.back
Navigiert in der Historie zurück. Entspricht dem Klicken auf die Zurück-Schaltfläche 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
Lädt die aktuelle URL neu. Entspricht dem Klicken auf die Aktualisieren-Schaltfläche 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 auf verschiedene Ereignisse im Next.js-Router hören. Hier eine Liste der unterstützten Ereignisse:
routeChangeStart(url, { shallow })
- Wird ausgelöst, wenn eine Route beginnt, sich zu ändernrouteChangeComplete(url, { shallow })
- Wird ausgelöst, wenn eine Route vollständig geändert wurderouteChangeError(err, url, { shallow })
- Wird ausgelöst, wenn ein Fehler beim Ändern der Routen auftritt oder ein Routenladen abgebrochen wirderr.cancelled
- Gibt an, ob die Navigation abgebrochen wurde
beforeHistoryChange(url, { shallow })
- Wird ausgelöst, bevor die Browser-Historie geändert wirdhashChangeStart(url, { shallow })
- Wird ausgelöst, wenn sich der Hash ändert, aber nicht die SeitehashChangeComplete(url, { shallow })
- Wird ausgelöst, wenn sich der Hash geändert hat, aber nicht die Seite
Wichtig: Hier ist
url
die im Browser angezeigte URL, einschließlich desbasePath
.
Um beispielsweise auf das Router-Ereignis routeChangeStart
zu hören, ö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'
} shallow routing`
)
}
router.events.on('routeChangeStart', handleRouteChange)
// Wenn die Komponente ausgehängt wird, vom Ereignis mit der 'off'-Methode abmelden:
return () => {
router.events.off('routeChangeStart', handleRouteChange)
}
}, [router])
return <Component {...pageProps} />
}
Wir verwenden eine Custom App (
pages/_app.js
) für dieses Beispiel, um das Ereignis zu abonnieren, weil sie bei Seitenwechseln nicht ausgehängt wird. Sie können Router-Ereignisse jedoch in jeder Komponente Ihrer Anwendung abonnieren.
Router-Ereignisse sollten registriert werden, wenn eine Komponente eingehängt wird (useEffect oder componentDidMount / componentWillUnmount) oder imperativ, wenn ein Ereignis eintritt.
Wenn ein Routenladen abgebrochen wird (z.B. durch schnelles Klicken auf zwei Links hintereinander), wird routeChangeError
ausgelöst. Das übergebene err
enthält eine Eigenschaft cancelled
, 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 ausgehängt wird, vom Ereignis mit der 'off'-Methode abmelden:
return () => {
router.events.off('routeChangeError', handleRouteChangeError)
}
}, [router])
return <Component {...pageProps} />
}
Potenzielle ESLint-Fehler
Bestimmte Methoden des router
-Objekts geben ein Promise zurück. Wenn die ESLint-Regel no-floating-promises aktiviert ist, sollten Sie diese entweder global oder für die betroffene Zeile deaktivieren.
Falls Ihre Anwendung diese Regel benötigt, sollten Sie entweder das Promise mit void
kennzeichnen – oder eine async
-Funktion verwenden, das Promise mit await
aufrufen und dann den Funktionsaufruf mit void
markieren. Dies gilt nicht, wenn die Methode innerhalb eines onClick
-Handlers 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 abfragen und zurückgeben
const useUser = () => ({ user: null, loading: false })
export default function Page() {
const { user, loading } = useUser()
const router = useRouter()
useEffect(() => {
// Deaktivieren der Linting-Regel in der nächsten Zeile - Dies ist die sauberste Lösung
// eslint-disable-next-line no-floating-promises
router.push('/login')
// Das von router.push zurückgegebene Promise mit void kennzeichnen
if (!(user || loading)) {
void router.push('/login')
}
// oder eine async-Funktion verwenden, das Promise mit await aufrufen und dann den Funktionsaufruf mit void markieren
async function handleRouteChange() {
if (!(user || loading)) {
await router.push('/login')
}
}
void handleRouteChange()
}, [user, loading])
return <p>Weiterleitung...</p>
}
withRouter
Falls useRouter
nicht die beste Option 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)