useRouter
Если вам нужно получить доступ к объекту router внутри любого функционального компонента вашего приложения, вы можете использовать хук useRouter, ознакомьтесь со следующим примером:
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является React хуком, что означает, что его нельзя использовать с классами. Вы можете либо использовать withRouter, либо обернуть свой класс в функциональный компонент.
объект маршрутизатора
Ниже приведено определение объекта router , возвращаемого как useRouter, так и withRouter:
-
pathname:String- Путь к текущему файлу маршрута, который следует за/pages. ПоэтомуbasePath,localeи заключительный слэш (trailingSlash: true) не включаются. -
query:Object- Строка запроса, преобразованная в объект, включая параметры динамических маршрутов. Будет пустым объектом во время предварительной рендерной обработки, если страница не использует рендеринг на стороне сервера. По умолчанию{} -
asPath:String- Путь, отображаемый в браузере, включая параметры поиска и учитывающий конфигурациюtrailingSlash.basePathиlocaleне включаются. -
isFallback:boolean- Признак, указывает, находится ли текущая страница в режиме fallback. -
basePath:String- Активный basePath (если включен). -
locale:String- Активный язык (если включен). -
locales:String[]- Все поддерживаемые языки (если включены). -
defaultLocale:String- Текущий язык по умолчанию (если включен). -
domainLocales:Array<{domain, defaultLocale, locales}>- Все настроенные языковые домены. -
isReady:boolean- Признак, указывающий, обновляются ли поля маршрутизатора на стороне клиента и готовы к использованию. Следует использовать только внутри методовuseEffectи не для условного рендеринга на сервере. См. соответствующую документацию для использования в случае с автоматически статически оптимизированными страницами -
isPreview:boolean- Признак, указывает, находится ли приложение в режиме предварительного просмотра.
Использование поля
asPathможет привести к несоответствию между клиентом и сервером, если страница рендерится с использованием рендеринга на стороне сервера или автоматической статической оптимизации. Избегайте использованияasPathдо тех пор, пока полеisReadyне будетtrue.
Следующие методы включены в router:
router.push
Обрабатывает переходы на стороне клиента, этот метод полезен в случаях, когда next/link недостаточно.
router.push(url, as, options)
-
url:UrlObject | String- URL для перехода (см. документацию модуля URL Node.JS для свойствUrlObject). -
as:UrlObject | String- Необязательный декоратор для пути, который будет отображен в адресной строке браузера. До Next.js 9.5.3 это использовалось для динамических маршрутов. -
options- Необязательный объект с настройками:-
scroll- Необязательный boolean, управляет прокруткой до начала страницы после перехода. По умолчаниюtrue -
shallow: Обновляет путь текущей страницы без повторного запускаgetStaticProps,getServerSidePropsилиgetInitialProps. По умолчаниюfalse -
locale- Необязательная строка, указывает язык новой страницы.
-
Для внешних URL-адресов вам не нужно использовать
router.push. window.location более подходит для таких случаев.
Переход к pages/about.js, что является предопределенным маршрутом:
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, который является динамическим маршрутом:
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, полезно для страниц, требующих аутентификации:
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>
}
Сброс состояния после перехода
При переходе к той же странице в Next.js состояние страницы не будет сбрасываться по умолчанию, так как React не размонтирует компонент, если не изменился родительский компонент.
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 не будут сбрасывать счётчик. Счётчик сохраняется между рендерами, так как верхний React-компонент Page остаётся тем же.
Если вам не нужно такое поведение, у вас есть несколько вариантов:
-
Убедитесь, что каждое состояние обновляется вручную, используя
useEffect. В приведенном выше примере это может выглядеть так:useEffect(() => { setCount(0) }, [router.query.slug]) -
Используйте React
keyдля того, чтобы указать React на повторное монтирование компонента. Для всех страниц можно использовать пользовательское приложение:import { useRouter } from 'next/router' export default function MyApp({ Component, pageProps }) { const router = useRouter() return <Component key={router.asPath} {...pageProps} /> }
С объектом URL
Вы можете использовать объект URL аналогичным образом, как и для next/link. Работает как для параметра url, так и для as:
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
Аналогично свойству replace в next/link, router.replace предотвратит добавление новой записи в стек URL history.
router.replace(url, as, options)
- API для
router.replaceточно такой же, как и API дляrouter.push.
Рассмотрим следующий пример:
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 автоматически выполняет предварительную загрузку страниц.
Эта функция доступна только в релизной сборке. Next.js не выполняет предварительную загрузку страниц в режиме разработки.
router.prefetch(url, as, options)
-
url- URL для предварительной загрузки, включая явные маршруты (например,/dashboard) и динамические маршруты (например,/product/[id]) -
as- Необязательный декоратор дляurl. До Next.js 9.5.3 это использовалось для предварительной загрузки динамических маршрутов. -
options- Необязательный объект со следующими разрешенными полями:-
locale- позволяет указать другой язык, отличный от активного. Еслиfalse,urlдолжен включать язык, так как активный язык не будет использован.
-
Предположим, у вас есть страница входа, и после входа вы перенаправляете пользователя на панель управления. В этом случае мы можем предварительно загрузить панель управления, чтобы переход был быстрее, как в следующем примере:
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}>
{/* Form fields */}
<button type="submit">Login</button>
</form>
)
}
router.beforePopState
В некоторых случаях (например, при использовании пользовательского сервера), вы можете прослушивать popstate и что-то сделать перед тем, как маршрутизатор отреагирует на него.
router.beforePopState(cb)
-
cb- Функция для выполнения при поступлении событийpopstate. Функция получает состояние события как объект со следующими свойствами:-
url:String- Маршрут для нового состояния. Обычно это имяpage -
as:String- URL, который будет отображаться в браузере. -
options:Object- Дополнительные параметры, отправленные методом router.push
-
Если cb возвращает false, маршрутизатор Next.js не обработает popstate, и вам нужно будет обработать его самостоятельно. См. Отключение маршрутизации по файловой системе.
Вы можете использовать beforePopState для изменения запроса или принудительного обновления SSR, как в следующем примере:
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().
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().
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. Вот список поддерживаемых событий:
-
routeChangeStart(url, { shallow })- Срабатывает, когда маршрут начинает изменяться -
routeChangeComplete(url, { shallow })- Срабатывает, когда маршрут полностью изменился -
routeChangeError(err, url, { shallow })- Срабатывает, когда при изменении маршрута возникает ошибка или загрузка маршрута отменена-
err.cancelled- Указывает, была ли навигация отменена
-
-
beforeHistoryChange(url, { shallow })- Срабатывает перед изменением истории браузера -
hashChangeStart(url, { shallow })- Срабатывает, когда хеш изменится, но страница нет -
hashChangeComplete(url, { shallow })- Срабатывает, когда хеш изменился, но страница нет
Важно знать: Здесь
urlотображается URL в браузере, включаяbasePath.
Например, чтобы прослушать событие маршрутизатора routeChangeStart, откройте или создайте pages/_app.js и подпишитесь на событие следующим образом:
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) для подписки на событие, потому что оно не размонтируется при переходе на другую страницу, но вы можете подписываться на события маршрутизатора на любом компоненте вашего приложения.
События маршрутизатора должны регистрироваться при монтировании компонента (useEffect или componentDidMount / componentWillUnmount) или императивно, когда происходит событие.
Если загрузка маршрута отменяется (например, при быстром последовательном щелчке по двум ссылкам), сработает routeChangeError. И переданное err будет содержать свойство cancelled со значением true, как в следующем примере:
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} />
}
Возможные ошибки ESLint
Определенные методы, доступные для объекта router, возвращают Promise. Если у вас включено правило ESLint no-floating-promises, рассмотрите возможность его отключения глобально или для соответствующей строки.
Если вашему приложению нужно это правило, вы должны либо void промис, либо использовать async функцию, await промис, а затем аннулировать вызов функции. Это не применимо, когда метод вызывается из обработчика onClick.
Затронутые методы:
router.pushrouter.replacerouter.prefetch
Возможные решения
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 объект к любому компоненту.
Использование
import { withRouter } from 'next/router'
function Page({ router }) {
return <p>{router.pathname}</p>
}
export default withRouter(Page)
TypeScript
Чтобы использовать компоненты класса с withRouter, компонент должен принимать свойство router:
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)
© 2024 Vercel, Inc.
Licensed under the MIT License.
https://nextjs.org/docs/pages/api-reference/functions/use-router