useRouter
如果你想访问应用中任何功能组件内的 router
对象,你可以使用 useRouter
钩子,请看以下示例:
¥If you want to access the router
object inside any function component in your app, you can use the useRouter
hook, 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} >
{children}
</a>
)
}
export default ActiveLink
useRouter
是 React 钩子,这意味着它不能与类一起使用。你可以使用 withRouter 或将你的类封装在函数组件中。¥
useRouter
is a React Hook, meaning it cannot be used with classes. You can either use withRouter or wrap your class in a function component.
router
对象
¥router
object
以下是 useRouter
和 withRouter
返回的 router
对象的定义:
¥The following is the definition of the router
object returned by both useRouter
and withRouter
:
-
pathname
:String
-/pages
之后的当前路由文件的路径。因此,不包括basePath
、locale
和尾部斜杠 (trailingSlash: true
)。¥
pathname
:String
- The path for current route file that comes after/pages
. Therefore,basePath
,locale
and trailing slash (trailingSlash: true
) are not included. -
query
:Object
- 查询字符串解析为对象,包含 动态路由 个参数。如果页面不使用 服务端渲染,则在预渲染期间它将是一个空对象。默认为{}
¥
query
:Object
- The query string parsed to an object, including dynamic route parameters. It will be an empty object during prerendering if the page doesn't use Server-side Rendering. Defaults to{}
-
asPath
:String
- 浏览器中显示的路径包括搜索参数并遵循trailingSlash
配置。不包括basePath
和locale
。¥
asPath
:String
- The path as shown in the browser including the search params and respecting thetrailingSlash
configuration.basePath
andlocale
are not included. -
isFallback
:boolean
- 当前页面是否在 后备模式。¥
isFallback
:boolean
- Whether the current page is in fallback mode. -
basePath
:String
- 活动 basePath(如果启用)。¥
basePath
:String
- The active basePath (if enabled). -
locale
:String
- 活动区域设置(如果启用)。¥
locale
:String
- The active locale (if enabled). -
locales
:String[]
- 所有受支持的区域设置(如果启用)。¥
locales
:String[]
- All supported locales (if enabled). -
defaultLocale
:String
- 当前的默认区域设置(如果启用)。¥
defaultLocale
:String
- The current default locale (if enabled). -
domainLocales
:Array<{domain, defaultLocale, locales}>
- 任何配置的域区域设置。¥
domainLocales
:Array<{domain, defaultLocale, locales}>
- Any configured domain locales. -
isReady
:boolean
- 路由字段是否已在客户端更新并可供使用。只能在useEffect
方法内部使用,不能用于服务器上的条件渲染。请参阅相关文档了解 自动静态优化页面 的用例¥
isReady
:boolean
- Whether the router fields are updated client-side and ready for use. Should only be used inside ofuseEffect
methods and not for conditionally rendering on the server. See related docs for use case with automatically statically optimized pages -
isPreview
:boolean
- 应用当前是否处于 预览模式。¥
isPreview
:boolean
- Whether the application is currently in preview mode.
如果页面使用服务器端渲染或 自动静态优化 渲染,则使用
asPath
字段可能会导致客户端和服务器之间不匹配。在isReady
字段为true
之前,避免使用asPath
。¥Using the
asPath
field may lead to a mismatch between client and server if the page is rendered using server-side rendering or automatic static optimization. Avoid usingasPath
until theisReady
field istrue
.
router
内部包含以下方法:
¥The following methods are included inside router
:
router.push
处理客户端转换,此方法对于 next/link
不够的情况很有用。
¥Handles client-side transitions, this method is useful for cases where next/link
is not enough.
router.push(url, as, options)
-
url
:UrlObject | String
- 要导航到的 URL(请参阅 Node.JS URL 模块文档 了解UrlObject
属性)。¥
url
:UrlObject | String
- The URL to navigate to (see Node.JS URL module documentation forUrlObject
properties). -
as
:UrlObject | String
- 将显示在浏览器 URL 栏中的路径的可选装饰器。在 Next.js 9.5.3 之前,这用于动态路由。¥
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. -
options
- 具有以下配置选项的可选对象:¥
options
- Optional object with the following configuration options:-
scroll
- 可选布尔值,控制导航后滚动到页面顶部。默认为true
¥
scroll
- Optional boolean, controls scrolling to the top of the page after navigation. Defaults totrue
-
shallow
:更新当前页面的路径,无需重新运行getStaticProps
、getServerSideProps
或getInitialProps
。默认为false
¥
shallow
: Update the path of the current page without rerunninggetStaticProps
,getServerSideProps
orgetInitialProps
. Defaults tofalse
-
locale
- 可选字符串,指示新页面的区域设置¥
locale
- Optional string, indicates locale of the new page
-
你不需要将
router.push
用于外部 URL。window.location 更适合这些情况。¥You don't need to use
router.push
for external URLs. window.location is better suited for those cases.
导航到 pages/about.js
,这是预定义的路由:
¥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>
)
}
导航 pages/post/[pid].js
,这是一条动态路由:
¥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>
)
}
将用户重定向到 pages/login.js
,对于 authentication 后面的页面很有用:
¥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>
}
导航后重置状态
¥Resetting state after navigation
当导航到 Next.js 中的同一页面时,默认情况下不会重置页面的状态,因为除非父组件发生更改,否则 React 不会卸载。
¥When navigating to the same page in Next.js, the page's state will not be reset by default as React does not unmount unless the parent component has changed.
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>Page: {router.query.slug}</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increase count</button>
<Link href="/one">one</Link> <Link href="/two">two</Link>
</div>
)
}
在上面的示例中,在 /one
和 /two
之间导航不会重置计数。 useState
在渲染之间保持不变,因为顶层 React 组件 Page
是相同的。
¥In the above example, navigating between /one
and /two
will not reset the count . The useState
is maintained between renders because the top-level React component, Page
, is the same.
如果你不希望出现这种行为,你有以下几种选择:
¥If you do not want this behavior, you have a couple of options:
-
使用
useEffect
手动确保每个状态都已更新。在上面的示例中,可能如下所示:¥Manually ensure each state is updated using
useEffect
. In the above example, that could look like:useEffect(() => {
setCount(0)
}, [router.query.slug]) -
使用 React
key
到 告诉 React 重新挂载组件。要对所有页面执行此操作,你可以使用自定义应用:¥Use a React
key
to tell React to remount the component. To do this for all pages, you can use a custom app:import { useRouter } from 'next/router'
export default function MyApp({ Component, pageProps }) {
const router = useRouter()
return <Component key={router.asPath} {...pageProps} />
}
带有 URL 对象
¥With URL object
你可以按照与 next/link
相同的方式使用 URL 对象。适用于 url
和 as
参数:
¥You can use a URL object in the same way you can use it for next/link
. Works for both the url
and as
parameters:
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
与 next/link
中的 replace
属性类似,router.replace
将阻止向 history
堆栈中添加新的 URL 条目。
¥Similar to the replace
prop in next/link
, router.replace
will prevent adding a new URL entry into the history
stack.
router.replace(url, as, options)
-
router.replace
的 API 与router.push
的 API 完全相同。¥The API for
router.replace
is exactly the same as the API forrouter.push
.
看一下下面的例子:
¥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
预取页面以实现更快的客户端转换。此方法仅适用于没有 next/link
的导航,因为 next/link
会自动处理预取页面。
¥Prefetch pages for faster client-side transitions. This method is only useful for navigations without next/link
, as next/link
takes care of prefetching pages automatically.
这是仅限生产的功能。Next.js 在开发中不会预取页面。
¥This is a production only feature. Next.js doesn't prefetch pages in development.
router.prefetch(url, as, options)
-
url
- 要预取的 URL,包括显式路由(例如/dashboard
)和动态路由(例如/product/[id]
)¥
url
- The URL to prefetch, including explicit routes (e.g./dashboard
) and dynamic routes (e.g./product/[id]
) -
as
-url
的可选装饰器。在 Next.js 9.5.3 之前,这用于预取动态路由。¥
as
- Optional decorator forurl
. Before Next.js 9.5.3 this was used to prefetch dynamic routes. -
options
- 具有以下允许字段的可选对象:¥
options
- Optional object with the following allowed fields:-
locale
- 允许提供与活动区域不同的区域设置。如果false
、url
必须包含区域设置,因为不会使用活动区域设置。¥
locale
- allows providing a different locale from the active one. Iffalse
,url
has to include the locale as the active locale won't be used.
-
假设你有一个登录页面,登录后,你将用户重定向到仪表板。对于这种情况,我们可以预取仪表板以实现更快的转换,如下例所示:
¥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')
}, [router])
return (
<form onSubmit={handleSubmit}>
<button type="submit">Login</button>
</form>
)
}
router.beforePopState
在某些情况下(例如,如果使用 自定义服务器),你可能希望监听 popstate 并在路由对其进行操作之前执行某些操作。
¥In some cases (for example, if using a Custom Server), you may wish to listen to popstate and do something before the router acts on it.
router.beforePopState(cb)
-
cb
- 在传入popstate
事件时运行的函数。该函数将事件状态作为具有以下属性的对象接收:¥
cb
- The function to run on incomingpopstate
events. The function receives the state of the event as an object with the following props:-
url
:String
- 新状态的路由。这通常是page
的名字¥
url
:String
- the route for the new state. This is usually the name of apage
-
as
:String
- 将在浏览器中显示的 url¥
as
:String
- the url that will be shown in the browser -
options
:Object
- router.push 发送的附加选项¥
options
:Object
- Additional options sent by router.push
-
如果 cb
返回 false
,Next.js 路由将不会处理 popstate
,在这种情况下你将负责处理它。参见 禁用文件系统路由。
¥If cb
returns 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.
你可以使用 beforePopState
来操纵请求,或强制 SSR 刷新,如下例所示:
¥You could use beforePopState
to 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
})
}, [router])
return <p>Welcome to the page</p>
}
router.back
回顾历史。相当于点击浏览器的后退按钮。它执行 window.history.back()
。
¥Navigate back in history. Equivalent to clicking the browser’s back button. It executes window.history.back()
.
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
重新加载当前 URL。相当于点击浏览器的刷新按钮。它执行 window.location.reload()
。
¥Reload the current URL. Equivalent to clicking the browser’s refresh button. It executes window.location.reload()
.
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
你可以监听 Next.js Router 内发生的不同事件。以下是支持的事件列表:
¥You can listen to different events happening inside the Next.js Router. Here's a list of supported events:
-
routeChangeStart(url, { shallow })
- 当路由开始改变时触发¥
routeChangeStart(url, { shallow })
- Fires when a route starts to change -
routeChangeComplete(url, { shallow })
- 当路由完全改变时触发¥
routeChangeComplete(url, { shallow })
- Fires when a route changed completely -
routeChangeError(err, url, { shallow })
- 当更改路由时出现错误或路由加载被取消时触发¥
routeChangeError(err, url, { shallow })
- Fires when there's an error when changing routes, or a route load is cancelled-
err.cancelled
- 指示导航是否已取消¥
err.cancelled
- Indicates if the navigation was cancelled
-
-
beforeHistoryChange(url, { shallow })
- 在更改浏览器历史记录之前触发¥
beforeHistoryChange(url, { shallow })
- Fires before changing the browser's history -
hashChangeStart(url, { shallow })
- 当哈希值发生变化但页面不变时触发¥
hashChangeStart(url, { shallow })
- Fires when the hash will change but not the page -
hashChangeComplete(url, { shallow })
- 当哈希值已更改但页面未更改时触发¥
hashChangeComplete(url, { shallow })
- Fires when the hash has changed but not the page
很高兴知道:这里的
url
是浏览器中显示的 URL,包括basePath
。¥Good to know: Here
url
is the URL shown in the browser, including thebasePath
.
例如,要监听路由事件 routeChangeStart
,请打开或创建 pages/_app.js
并订阅该事件,如下所示:
¥For example, to listen to the router event routeChangeStart
, open or create pages/_app.js
and 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)
}
}, [router])
return <Component {...pageProps} />
}
我们在本示例中使用 自定义应用 (
pages/_app.js
) 来订阅事件,因为它不会在页面导航上卸载,但你可以在应用中的任何组件上订阅路由事件。¥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.
路由事件应在组件安装(useEffect 或 componentDidMount / componentWillUnmount)时注册,或者在事件发生时强制注册。
¥Router events should be registered when a component mounts (useEffect or componentDidMount / componentWillUnmount) or imperatively when an event happens.
如果路由加载被取消(例如,通过连续快速单击两个链接),routeChangeError
将触发。并且传递的 err
将包含设置为 true
的 cancelled
属性,如下例所示:
¥If a route load is cancelled (for example, by clicking two links rapidly in succession), routeChangeError
will fire. And the passed err
will contain a cancelled
property 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)
}
}, [router])
return <Component {...pageProps} />
}
next/compat/router
导出
¥The next/compat/router
export
这是相同的 useRouter
钩子,但可以在 app
和 pages
目录中使用。
¥This is the same useRouter
hook, but can be used in both app
and pages
directories.
它与 next/router
的不同之处在于,当未安装页面路由时,它不会抛出错误,而是具有返回类型 NextRouter | null
。这允许开发者在过渡到 app
路由时转换组件以支持在 app
和 pages
中运行。
¥It differs from next/router
in that it does not throw an error when the pages router is not mounted, and instead has a return type of NextRouter | null
.
This allows developers to convert components to support running in both app
and pages
as they transition to the app
router.
以前看起来像这样的组件:
¥A component that previously looked like this:
import { useRouter } from 'next/router'
const MyComponent = () => {
const { isReady, query } = useRouter()
// ...
}
转换为 next/compat/router
时会出错,因为 null
无法解构。相反,开发者将能够利用新的钩子:
¥Will error when converted over to next/compat/router
, as null
can not be destructured. Instead, developers will be able to take advantage of new hooks:
import { useEffect } from 'react'
import { useRouter } from 'next/compat/router'
import { useSearchParams } from 'next/navigation'
const MyComponent = () => {
const router = useRouter() // may be null or a NextRouter instance
const searchParams = useSearchParams()
useEffect(() => {
if (router && !router.isReady) {
return
}
// In `app/`, searchParams will be ready immediately with the values, in
// `pages/` it will be available after the router is ready.
const search = searchParams.get('search')
// ...
}, [router, searchParams])
// ...
}
此组件现在可在 pages
和 app
目录中工作。当组件不再在 pages
中使用时,你可以删除对兼容路由的引用:
¥This component will now work in both pages
and app
directories. When the component is no longer used in pages
, you can remove the references to the compat router:
import { useSearchParams } from 'next/navigation'
const MyComponent = () => {
const searchParams = useSearchParams()
// As this component is only used in `app/`, the compat router can be removed.
const search = searchParams.get('search')
// ...
}
在页面中的 Next.js 上下文之外使用 useRouter
¥Using useRouter
outside of Next.js context in pages
另一个特定用例是在 Next.js 应用上下文之外渲染组件时,例如在 pages
目录上的 getServerSideProps
内部。在这种情况下,可以使用兼容路由来避免错误:
¥Another specific use case is when rendering components outside of a Next.js application context, such as inside getServerSideProps
on the pages
directory. In this case, the compat router can be used to avoid errors:
import { renderToString } from 'react-dom/server'
import { useRouter } from 'next/compat/router'
const MyComponent = () => {
const router = useRouter() // may be null or a NextRouter instance
// ...
}
export async function getServerSideProps() {
const renderedComponent = renderToString(<MyComponent />)
return {
props: {
renderedComponent,
},
}
}
潜在的 ESLint 错误
¥Potential ESLint errors
router
对象上可访问的某些方法会返回 Promise。如果你启用了 ESLint 规则 no-floating-promises,请考虑全局禁用它或针对受影响的行禁用它。
¥Certain methods accessible on the router
object return a Promise. If you have the ESLint rule, no-floating-promises enabled, consider disabling it either globally, or for the affected line.
如果你的应用需要此规则,你应该 void
promise - 或使用 async
函数,await
promise,然后无效函数调用。当从 onClick
处理程序内部调用该方法时,这不适用。
¥If your application needs this rule, you should either void
the promise – or use an async
function, await
the Promise, then void the function call. This is not applicable when the method is called from inside an onClick
handler.
受影响的方法有:
¥The affected methods are:
-
router.push
-
router.replace
-
router.prefetch
潜在的解决方案
¥Potential solutions
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(() => {
// disable the linting on the next line - This is the cleanest solution
// eslint-disable-next-line no-floating-promises
router.push('/login')
// void the Promise returned by router.push
if (!(user || loading)) {
void router.push('/login')
}
// or use an async function, await the Promise, then void the function call
async function handleRouteChange() {
if (!(user || loading)) {
await router.push('/login')
}
}
void handleRouteChange()
}, [user, loading])
return <p>Redirecting...</p>
}
withRouter
如果 useRouter
不是最适合你,withRouter
也可以将相同的 router
对象 添加到任何组件。
¥If useRouter
is not the best fit for you, withRouter
can 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
要将类组件与 withRouter
一起使用,该组件需要接受 router 属性:
¥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)