useRouter
Si deseas acceder al objeto router
dentro de cualquier componente de función en tu aplicación, puedes usar el hook useRouter
. Observa el siguiente ejemplo:
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
es un Hook de React, lo que significa que no puede usarse con clases. Puedes usar withRouter o envolver tu clase en un componente de función.
Objeto router
A continuación se muestra la definición del objeto router
devuelto tanto por useRouter
como por withRouter
:
pathname
:String
- La ruta del archivo de ruta actual que viene después de/pages
. Por lo tanto, no incluyebasePath
,locale
ni la barra diagonal final (trailingSlash: true
).query
:Object
- La cadena de consulta analizada como un objeto, incluyendo parámetros de rutas dinámicas. Será un objeto vacío durante la prerenderización si la página no usa Renderizado del lado del servidor (SSR). Por defecto es{}
.asPath
:String
- La ruta como se muestra en el navegador, incluyendo los parámetros de búsqueda y respetando la configuración detrailingSlash
. No incluyebasePath
nilocale
.isFallback
:boolean
- Indica si la página actual está en modo de fallback.basePath
:String
- El basePath activo (si está habilitado).locale
:String
- La configuración regional (locale) activa (si está habilitada).locales
:String[]
- Todas las configuraciones regionales soportadas (si están habilitadas).defaultLocale
:String
- La configuración regional predeterminada actual (si está habilitada).domainLocales
:Array<{domain, defaultLocale, locales}>
- Cualquier configuración regional de dominio configurada.isReady
:boolean
- Indica si los campos del enrutador se han actualizado en el lado del cliente y están listos para su uso. Solo debe usarse dentro de métodosuseEffect
y no para renderizar condicionalmente en el servidor. Consulta la documentación relacionada para casos de uso con páginas estáticamente optimizadas de forma automática.isPreview
:boolean
- Indica si la aplicación está actualmente en modo de vista previa.
Usar el campo
asPath
puede provocar una discrepancia entre el cliente y el servidor si la página se renderiza usando renderizado del lado del servidor o optimización estática automática. Evita usarasPath
hasta que el campoisReady
seatrue
.
Los siguientes métodos están incluidos dentro de router
:
router.push
Ejemplos
Maneja transiciones del lado del cliente. Este método es útil para casos donde next/link
no es suficiente.
router.push(url, as, options)
url
:UrlObject | String
- La URL a la que navegar (consulta la documentación del módulo URL de Node.JS para las propiedades deUrlObject
).as
:UrlObject | String
- Decorador opcional para la ruta que se mostrará en la barra de URL del navegador. Antes de Next.js 9.5.3, esto se usaba para rutas dinámicas.options
- Objeto opcional con las siguientes opciones de configuración:scroll
- Booleano opcional, controla el desplazamiento a la parte superior de la página después de la navegación. Por defecto estrue
.shallow
: Actualiza la ruta de la página actual sin volver a ejecutargetStaticProps
,getServerSideProps
ogetInitialProps
. Por defecto esfalse
.locale
- Cadena opcional, indica la configuración regional de la nueva página.
No necesitas usar
router.push
para URLs externas. window.location es más adecuado para esos casos.
Navegando a pages/about.js
, que es una ruta predefinida:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/about')}>
Haz clic aquí
</button>
)
}
Navegando a pages/post/[pid].js
, que es una ruta dinámica:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/post/abc')}>
Haz clic aquí
</button>
)
}
Redirigiendo al usuario a pages/login.js
, útil para páginas detrás de autenticación:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Aquí obtendrías y devolverías el usuario
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>Redirigiendo...</p>
}
Reiniciando el estado después de la navegación
Al navegar a la misma página en Next.js, el estado de la página no se reiniciará por defecto, ya que React no desmonta a menos que el componente padre haya cambiado.
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>Página: {router.query.slug}</h1>
<p>Contador: {count}</p>
<button onClick={() => setCount(count + 1)}>Incrementar contador</button>
<Link href="/one">uno</Link> <Link href="/two">dos</Link>
</div>
)
}
En el ejemplo anterior, navegar entre /one
y /two
no reiniciará el contador. El useState
se mantiene entre renderizados porque el componente de nivel superior de React, Page
, es el mismo.
Si no deseas este comportamiento, tienes un par de opciones:
-
Actualiza manualmente cada estado usando
useEffect
. En el ejemplo anterior, podría verse así:useEffect(() => { setCount(0) }, [router.query.slug])
-
Usa una
key
de React para indicar a React que remonte el componente. Para hacer esto en todas las páginas, puedes usar una aplicación personalizada:pages/_app.js import { useRouter } from 'next/router' export default function MyApp({ Component, pageProps }) { const router = useRouter() return <Component key={router.asPath} {...pageProps} /> }
Con objeto URL
Puedes usar un objeto URL de la misma manera que lo haces para next/link
. Funciona tanto para el parámetro url
como para 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 },
})
}}
>
Haz clic aquí para leer más
</button>
)
}
router.replace
Similar a la propiedad replace
en next/link
, router.replace
evitará agregar una nueva entrada de URL a la pila de history
.
router.replace(url, as, options)
- La API para
router.replace
es exactamente la misma que la API pararouter.push
.
Observa el siguiente ejemplo:
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.replace('/home')}>
Haz clic aquí
</button>
)
}
router.prefetch
Prefetch de páginas para transiciones más rápidas del lado del cliente. Este método solo es útil para navegaciones sin next/link
, ya que next/link
se encarga del prefetch de páginas automáticamente.
Esta es una característica solo para producción. Next.js no hace prefetch de páginas en desarrollo.
router.prefetch(url, as, options)
url
- La URL para hacer prefetch, incluyendo rutas explícitas (ej./dashboard
) y rutas dinámicas (ej./product/[id]
).as
- Decorador opcional paraurl
. Antes de Next.js 9.5.3, esto se usaba para hacer prefetch de rutas dinámicas.options
- Objeto opcional con los siguientes campos permitidos:locale
- Permite proporcionar una configuración regional diferente a la activa. Si esfalse
,url
debe incluir la configuración regional, ya que no se usará la activa.
Supongamos que tienes una página de inicio de sesión y, después del inicio de sesión, rediriges al usuario al panel de control. Para ese caso, podemos hacer prefetch del panel de control para hacer una transición más rápida, como en el siguiente ejemplo:
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({
/* Datos del formulario */
}),
}).then((res) => {
// Haz una transición rápida del lado del cliente a la página del panel ya prefetcheada
if (res.ok) router.push('/dashboard')
})
}, [])
useEffect(() => {
// Prefetch de la página del panel
router.prefetch('/dashboard')
}, [router])
return (
<form onSubmit={handleSubmit}>
{/* Campos del formulario */}
<button type="submit">Iniciar sesión</button>
</form>
)
}
router.beforePopState
En algunos casos (por ejemplo, si usas un Servidor Personalizado), es posible que desees escuchar popstate y hacer algo antes de que el enrutador actúe sobre él.
router.beforePopState(cb)
cb
- La función a ejecutar en eventospopstate
entrantes. La función recibe el estado del evento como un objeto con las siguientes propiedades:url
:String
- la ruta para el nuevo estado. Suele ser el nombre de unapage
.as
:String
- la URL que se mostrará en el navegador.options
:Object
- Opciones adicionales enviadas por router.push.
Si cb
devuelve false
, el enrutador de Next.js no manejará popstate
, y serás responsable de manejarlo en ese caso. Consulta Deshabilitar el enrutamiento del sistema de archivos.
Podrías usar beforePopState
para manipular la solicitud o forzar una actualización SSR, como en el siguiente ejemplo:
import { useEffect } from 'react'
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
useEffect(() => {
router.beforePopState(({ url, as, options }) => {
// ¡Solo quiero permitir estas dos rutas!
if (as !== '/' && as !== '/other') {
// Haz que SSR renderice rutas incorrectas como 404.
window.location.href = as
return false
}
return true
})
}, [router])
return <p>Bienvenido a la página</p>
}
router.back
Navega hacia atrás en el historial. Equivalente a hacer clic en el botón de retroceso del navegador. Ejecuta window.history.back()
.
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.back()}>
Haz clic aquí para retroceder
</button>
)
}
router.reload
Recarga la URL actual. Equivalente a hacer clic en el botón de actualización del navegador. Ejecuta window.location.reload()
.
import { useRouter } from 'next/router'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.reload()}>
Haz clic aquí para recargar
</button>
)
}
router.events
Ejemplos
Puedes escuchar diferentes eventos que ocurren dentro del Enrutador (Router) de Next.js. Aquí hay una lista de eventos soportados:
routeChangeStart(url, { shallow })
- Se dispara cuando una ruta comienza a cambiar.routeChangeComplete(url, { shallow })
- Se dispara cuando una ruta ha cambiado completamente.routeChangeError(err, url, { shallow })
- Se dispara cuando hay un error al cambiar rutas o se cancela la carga de una ruta.err.cancelled
- Indica si la navegación fue cancelada.
beforeHistoryChange(url, { shallow })
- Se dispara antes de cambiar el historial del navegador.hashChangeStart(url, { shallow })
- Se dispara cuando el hash cambiará pero no la página.hashChangeComplete(url, { shallow })
- Se dispara cuando el hash ha cambiado pero no la página.
Importante: Aquí
url
es la URL mostrada en el navegador, incluyendo elbasePath
.
Por ejemplo, para escuchar el evento del enrutador routeChangeStart
, abre o crea pages/_app.js
y suscríbete al evento, así:
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(
`La aplicación está cambiando a ${url} ${
shallow ? 'con' : 'sin'
} enrutamiento superficial (shallow routing)`
)
}
router.events.on('routeChangeStart', handleRouteChange)
// Si el componente se desmonta, cancela la suscripción
// del evento con el método 'off':
return () => {
router.events.off('routeChangeStart', handleRouteChange)
}
}, [router])
return <Component {...pageProps} />
}
Usamos una Aplicación Personalizada (
pages/_app.js
) para este ejemplo para suscribirnos al evento porque no se desmonta en navegaciones de página, pero puedes suscribirte a eventos del enrutador en cualquier componente de tu aplicación.
Los eventos del enrutador deben registrarse cuando un componente se monta (useEffect o componentDidMount / componentWillUnmount) o imperativamente cuando ocurre un evento.
Si se cancela la carga de una ruta (por ejemplo, al hacer clic en dos enlaces rápidamente en sucesión), se disparará routeChangeError
. Y el err
pasado contendrá una propiedad cancelled
establecida en true
, como en el siguiente ejemplo:
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(`La ruta a ${url} fue cancelada!`)
}
}
router.events.on('routeChangeError', handleRouteChangeError)
// Si el componente se desmonta, cancela la suscripción
// del evento con el método 'off':
return () => {
router.events.off('routeChangeError', handleRouteChangeError)
}
}, [router])
return <Component {...pageProps} />
}
Errores potenciales de ESLint
Ciertos métodos accesibles en el objeto router
devuelven una Promesa. Si tiene habilitada la regla de ESLint no-floating-promises, considere desactivarla globalmente o para la línea afectada.
Si su aplicación necesita esta regla, debería usar void
con la promesa o utilizar una función async
, hacer await
de la Promesa y luego usar void
con la llamada a la función. Esto no aplica cuando el método se llama desde dentro de un manejador onClick
.
Los métodos afectados son:
router.push
router.replace
router.prefetch
Soluciones potenciales
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Aquí obtendrías y devolverías el usuario
const useUser = () => ({ user: null, loading: false })
export default function Page() {
const { user, loading } = useUser()
const router = useRouter()
useEffect(() => {
// deshabilitar el linting en la siguiente línea - Esta es la solución más limpia
// eslint-disable-next-line no-floating-promises
router.push('/login')
// usar void con la Promesa devuelta por router.push
if (!(user || loading)) {
void router.push('/login')
}
// o usar una función async, hacer await de la Promesa y luego usar void con la llamada a la función
async function handleRouteChange() {
if (!(user || loading)) {
await router.push('/login')
}
}
void handleRouteChange()
}, [user, loading])
return <p>Redirigiendo...</p>
}
withRouter
Si useRouter
no es la mejor opción para usted, withRouter
también puede agregar el mismo objeto router
a cualquier componente.
Uso
import { withRouter } from 'next/router'
function Page({ router }) {
return <p>{router.pathname}</p>
}
export default withRouter(Page)
TypeScript
Para usar componentes de clase con withRouter
, el componente necesita aceptar una prop 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)