Next.js: Router.push('/link') no se desplaza hacia la parte superior de la página cuando se activa

Creado en 7 nov. 2017  ·  25Comentarios  ·  Fuente: vercel/next.js

Hola,

tal vez me estoy perdiendo algo, pero cuando uso esta función y redirijo a otra página, el desplazamiento no se reinicia a la parte superior, pero la página se carga desplazada hacia el medio, sé por qué ocurre este problema, pero ¿puedo solucionarlo de alguna manera con alguna bandera como JAVASCRIPT Router.pusht({pathname: '/link', scrollreset: true}) o algo así, no encontré nada similar en la documentación

Comentario más útil

Estoy de acuerdo en que debería haber una opción scrollreset . Si no quieres hacerlo en componentDidMount también puedes hacerlo Router.push('/link').then(() => window.scrollTo(0, 0)); .

Todos 25 comentarios

el usuario decide si debe desplazarse hacia arriba o no. Creo que puedes hacerlo en componentDidMount

Estoy de acuerdo en que debería haber una opción scrollreset . Si no quieres hacerlo en componentDidMount también puedes hacerlo Router.push('/link').then(() => window.scrollTo(0, 0)); .

Router.push('/link').then(() => window.scrollTo(0, 0)); esta es la forma correcta de hacerlo, siéntase libre de agregarlo al archivo Léame @gragland ❤️

Gracias @gralagland , tu solución no parece tan fea, pero sería bueno tener esa opción para mantener el principio SECO. La única solución rápida por ahora es hacer una plantilla en vivo para escribir esto 😅

Guau... ¡Quiere decir que necesito revisar cada página para agregar .then() ! Por cierto, estoy usando next-routes , entonces, ¿hay alguna manera de agregar esto globalmente, solo para agregar este código una vez, como addRequestTransform si necesita agregar algunos encabezados antes de llamar a una API?

@MBach también puede enganchar window.scrollTo(0, 0) en componentDidMount del componente objetivo.

Solo quiero intervenir y decir que encontré este comportamiento extremadamente sorprendente. He estado creando sitios de producción con Next.js durante casi un año y recién ahora descubrí que Router.push no volvía a la cima. Siempre pensé que Router.push se comportaba exactamente como <Link> pero era solo la forma programática de hacerlo.

Y en términos de comportamiento predeterminado coincidente como <a> vs. <Link> , el equivalente no SPA de Router.push parecería estar configurando window.location que también le devuelve en la parte superior de la página, ¿no? Tendría más sentido como el comportamiento predeterminado para mí.

@gragland no funciona

Para que funcione globalmente, puede usar el detector de eventos del enrutador next.js incorporado: Router.events.on('routeChangeComplete', () => { window.scrollTo(0, 0); }); .
Simplemente coloque esto en un componente compartido en todas las páginas, por ejemplo, el encabezado.

@timneutkens ¿Hay alguna razón por la que el comportamiento de desplazamiento predeterminado para el imperativo Router.push y <Link> sea ​​diferente? Me parece un error obvio que se comporten de manera diferente.

Router.back() no devuelve la promesa, pero tiene el mismo problema, ¿qué hacer, desplazarse manualmente hacia arriba?

@alexsenichev ¿Probó la solución @macmenak ?

<Link href="/product/[id]" as={ /producto/${artículo.id} } beforePopState={()=>{ window.screenTop = 0; }}> <a> <h6>{item.name}</h6> </a> </Link>

Hola. ¿Por qué tendríamos que el componente Link predeterminado se desplace hacia arriba true pero Router.push no?

Creo que se debe revisar este tema.

Lo siento, pero etiquetando a los colaboradores: @liweinan0423 @timneutkens @exogen @goldenshun

Sería bueno si hubiera una opción en router.push() para forzar el desplazamiento hacia arriba, pero no creo que deba ser el valor predeterminado, ya que sería un cambio importante para todos los que ya asumen que no hace esto.

@ markjackson02 No veo cómo esto sería un cambio importante, ya que esta es la forma predeterminada en que el navegador manejaría esto. No estoy de acuerdo con el hecho de que tengamos dos enfoques diferentes para la misma funcionalidad.

Definitivamente sería un cambio radical. Las personas han estado creando aplicaciones tal como son ahora y esperan que funcionen de esa manera.

No estoy en contra de que ambos tengan la misma funcionalidad predeterminada, pero no creo que valga la pena el esfuerzo de migración.

De todos modos, este problema está cerrado, por lo que deberíamos abrir uno nuevo con lo que queramos hacer.

@markjackson02 OMHO, ¡el cambio más importante es que todavía no funciona de esa manera! El mal comportamiento ya está hecho. Así que creo que sería una buena idea ponerlo de la manera que debería. Pero estoy de acuerdo contigo, los contribuyentes probablemente no prestarán atención aquí.

@Timer @timneutkens Además, me mordió la diferencia en el comportamiento de Link y Router.push . FWIW, +1 para agregar a options de Router.push ya que eso lo mantendría compatible con versiones anteriores.

Puede usar esto para enrutar y desplazarse hasta la parte superior de la página:

// route using nextjs router and scroll to the top of the page
const routeToTop = (router, ...args) => {
    if(typeof window !== 'undefined') window.scrollTo(0, 0)
    return router.push.apply(router, args)
}

// usage

const router = useRouter()

routeToTop(router, '/my/page')

routeToTop(router, '/articles/[slug]', `/articles/${article.slug}`)

routeToTop(router, {
    pathname: '/search',
    query: {q: searchQuery},
})

Por cierto, esta no es una experiencia de usuario tan agradable como el comportamiento Link , ya que se desplaza a la parte superior de la página inmediatamente en lugar de cuando se carga la siguiente página.

Agregué un problema relacionado aquí para aquellos interesados: https://github.com/vercel/next.js/issues/15206

FWIW, aquí está mi opinión sobre una solución que se compartió en un problema relacionado.

import { useRouter } from 'next/router';
import { useEffect } from 'react';

/**
 * React hook that forces a scroll reset to a particular set of coordinates in the document
 * after `next/router` finishes transitioning to a new page client side. It smoothly scrolls back to
 * the top by default.
 *
 * <strong i="6">@see</strong> https://github.com/vercel/next.js/issues/3249
 * <strong i="7">@see</strong> https://github.com/vercel/next.js/issues/15206
 * <strong i="8">@see</strong> https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions
 * <strong i="9">@param</strong> {ScrollOptions} [options={}] Hook options.
 * <strong i="10">@param</strong> {ScrollBehavior} [options.behavior='smooth'] Specifies whether the scrolling should animate smoothly,
 *  or happen instantly in a single jump.
 * <strong i="11">@param</strong> {number} [options.left=0] Specifies the number of pixels along the X axis to scroll the window.
 * <strong i="12">@param</strong> {number} [options.top=0] Specifies the number of pixels along the Y axis to scroll the window.
 */
function useRouterScroll({ behavior = 'smooth', left = 0, top = 0 } = {}) {
  const router = useRouter();
  useEffect(() => {
    // Scroll to given coordinates when router finishes navigating
    // This fixes an inconsistent behaviour between `<Link/>` and `next/router`
    // See https://github.com/vercel/next.js/issues/3249
    const handleRouteChangeComplete = () => {
      window.scrollTo({ top, left, behavior });
    };

    router.events.on('routeChangeComplete', handleRouteChangeComplete);

    // If the component is unmounted, unsubscribe from the event
    return () => {
      router.events.off('routeChangeComplete', handleRouteChangeComplete);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [behavior, left, top]);
}

export default useRouterScroll;

Úselo en algún lugar en la parte superior de su árbol de componentes ( _app.js funciona).

// _app.js
function MyApp({ Component, pageProps }) {
  // Make sure pages scroll to the top after we navigate to them using `next/router`
  useRouterScroll();

  /* ... */
}

export default MyApp;

@nfantone ¿cuándo cambian estos campos? behavior, left, top . En adelante y atrás no funciona para mí.

@rakeshshubhu Esto solo afecta la navegación a través next/router .

Guau... ¡Quiere decir que necesito revisar cada página para agregar .then() ! Por cierto, estoy usando next-routes , entonces, ¿hay alguna manera de agregar esto globalmente, solo para agregar este código una vez, como addRequestTransform si necesita agregar algunos encabezados antes de llamar a una API?

No tienes que hacerlo. En cambio, creo un módulo y lo exporto a aquellos componentes que lo necesitan. Entonces, una vez que necesita cambiar, cambia ese único lugar.

También nota que estoy pasando el enrutador como parámetro ya que solo puede usar React Hooks, por ejemplo, useRouter() dentro de un componente

export const navigate = (event, url, router) => {
  event.preventDefault();
  event.stopPropagation();

  // Force scrolling to top
  router.push(url).then(() => window.scrollTo(0, 0));
};
¿Fue útil esta página
0 / 5 - 0 calificaciones