# 路由
Before moving forward, we recommend you to read
Routing Introductionfirst.
# useRouter
If you want to access the router object inside any function component in your app, you can use the useRouterhook, take a look at the following example:
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
useRouteris aReact Hook, meaning it cannot be used with classes. You can either usewithRouteror wrap your class in a function component.
# router object
The following is the definition of the routerobject returned by both useRouter and withRouter :
pathname:String- Current route. That is the path of the page in/pages, the configuredbasePathorlocaleis not included.query:Object- The query string parsed to an object. It will be an empty object during prerendering if the page doesn't havedata fetching requirements. Defaults to{}asPath:String- The path (including the query) shown in the browser without the configuredbasePathorlocale.isFallback:boolean- Whether the current page is infallback mode.basePath:String- The activebasePath(if enabled).locale:String- The active locale (if enabled).locales:String[]- All supported locales (if enabled).defaultLocale:String- The current default locale (if enabled).domainLocales:Array<{domain, defaultLocale, locales}>- Any configured domain locales.isReady:boolean- Whether the router fields are updated client-side and ready for use. Should only be used inside ofuseEffectmethods and not for conditionally rendering on the server.isPreview:boolean- Whether the application is currently inpreview mode.
Additionally, the following methods are also included inside router:
# router.push
Examples
Using Router
Handles client-side transitions, this method is useful for cases where next/link is not enough.
router.push(url, as, options)
url:UrlObject | String- The URL to navigate to (seeNode.JS URL module documentationforUrlObjectproperties).as:UrlObject | String- Optional decorator for the path that will be shown in the browser URL bar. Before Next.js 9.5.3 this was used for dynamic routes, check ourprevious docsto see how it worked. Note: when this path differs from the one provided inhrefthe previoushref/asbehavior is used as shown in theprevious docsoptions- Optional object with the following configuration options:scroll- Optional boolean, controls scrolling to the top of the page after navigation. Defaults totrueshallow: Update the path of the current page without rerunninggetStaticProps,getServerSidePropsorgetInitialProps. Defaults tofalselocale- Optional string, indicates locale of the new page
You don't need to use
router.pushfor external URLs.window.locationis better suited for those cases.
# Usage
Navigating to pages/about.js, which is a predefined route:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/about')}>
Click me
</button>
)
}
Navigating pages/post/[pid].js, which is a dynamic route:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/post/abc')}>
Click me
</button>
)
}
**Note:**When navigating to the same page in Next.js, the page's state will notbe reset by default, as the top-level React component is the same. You can manually ensure the state is updated using
useEffect.
Redirecting the user to pages/login.js, useful for pages behind authentication :
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Here you would fetch and return the user
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>Redirecting...</p>
}
# With URL object
You can use a URL object in the same way you can use it for next/link . Works for both the urland asparameters:
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 },
})
}}
>
Click here to read more
</button>
)
}
# router.replace
Similar to the replaceprop in next/link , router.replacewill prevent adding a new URL entry into the historystack.
router.replace(url, as, options)
- The API for
router.replaceis exactly the same as the API forrouter.push.
# Usage
Take a look at the following example:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.replace('/home')}>
Click me
</button>
)
}
# router.prefetch
Prefetch pages for faster client-side transitions. This method is only useful for navigations without next/link , as next/linktakes care of prefetching pages automatically.
This is a production only feature. Next.js doesn't prefetch pages on development.
router.prefetch(url, as)
url- The URL to prefetch, including explicit routes (e.g./dashboard) and dynamic routes (e.g./product/[id])as- Optional decorator forurl. Before Next.js 9.5.3 this was used to prefetch dynamic routes, check ourprevious docsto see how it worked
# Usage
Let's say you have a login page, and after a login, you redirect the user to the dashboard. For that case, we can prefetch the dashboard to make a faster transition, like in the following example:
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({
/* Form data */
}),
}).then((res) => {
// Do a fast client-side transition to the already prefetched dashboard page
if (res.ok) router.push('/dashboard')
})
}, [])
useEffect(() => {
// Prefetch the dashboard page
router.prefetch('/dashboard')
}, [])
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
<button type="submit">Login</button>
</form>
)
}
# router.beforePopState
In some cases (for example, if using a Custom Server](/docs/advanced-features/custom-server) ), you may wish to listen to [popstate and do something before the router acts on it.
router.beforePopState(cb)
cb- The function to run on incomingpopstateevents. The function receives the state of the event as an object with the following props:url:String- the route for the new state. This is usually the name of apageas:String- the url that will be shown in the browseroptions:Object- Additional options sent byrouter.push
If cbreturns false, the Next.js router will not handle popstate, and you'll be responsible for handling it in that case. See Disabling file-system routing .
# Usage
You could use beforePopStateto manipulate the request, or force a SSR refresh, as in the following example:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
useEffect(() => {
router.beforePopState(({ url, as, options }) => {
// I only want to allow these two routes!
if (as !== '/' && as !== '/other') {
// Have SSR render bad routes as a 404.
window.location.href = as
return false
}
return true
})
}, [])
return <p>Welcome to the page</p>
}
# router.back
Navigate back in history. Equivalent to clicking the browser’s back button. It executes window.history.back().
# Usage
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.back()}>
Click here to go back
</button>
)
}
# router.reload
Reload the current URL. Equivalent to clicking the browser’s refresh button. It executes window.location.reload().
# Usage
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.reload()}>
Click here to reload
</button>
)
}
# router.events
Examples
With a page loading indicator
You can listen to different events happening inside the Next.js Router. Here's a list of supported events:
routeChangeStart(url, { shallow })- Fires when a route starts to changerouteChangeComplete(url, { shallow })- Fires when a route changed completelyrouteChangeError(err, url, { shallow })- Fires when there's an error when changing routes, or a route load is cancellederr.cancelled- Indicates if the navigation was cancelledbeforeHistoryChange(url, { shallow })- Fires before changing the browser's historyhashChangeStart(url, { shallow })- Fires when the hash will change but not the pagehashChangeComplete(url, { shallow })- Fires when the hash has changed but not the page
**Note:**Here
urlis the URL shown in the browser, including thebasePath.
# Usage
For example, to listen to the router event routeChangeStart, open or create pages/_app.jsand subscribe to the event, like so:
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 is changing to ${url} ${
shallow ? 'with' : 'without'
} shallow routing`
)
}
router.events.on('routeChangeStart', handleRouteChange)
// If the component is unmounted, unsubscribe
// from the event with the `off` method:
return () => {
router.events.off('routeChangeStart', handleRouteChange)
}
}, [])
return <Component {...pageProps} />
}
We use a
Custom App(pages/_app.js) for this example to subscribe to the event because it's not unmounted on page navigations, but you can subscribe to router events on any component in your application.
Router events should be registered when a component mounts (useEffect or componentDidMount / componentWillUnmount ) or imperatively when an event happens.
If a route load is cancelled (for example, by clicking two links rapidly in succession), routeChangeErrorwill fire. And the passed errwill contain a cancelledproperty set to true, as in the following example:
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 to ${url} was cancelled!`)
}
}
router.events.on('routeChangeError', handleRouteChangeError)
// If the component is unmounted, unsubscribe
// from the event with the `off` method:
return () => {
router.events.off('routeChangeError', handleRouteChangeError)
}
}, [])
return <Component {...pageProps} />
}
# withRouter
If useRouter is not the best fit for you, withRoutercan also add the same router object to any component.
# Usage
import { withRouter } from 'next/router'
function Page({ router }) {
return <p>{router.pathname}</p>
}
export default withRouter(Page)
# TypeScript
To use class components with withRouter, the component needs to accept a router prop:
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)