Next.js: Router.push('/link') não rola o topo da página quando acionado

Criado em 7 nov. 2017  ·  25Comentários  ·  Fonte: vercel/next.js

Olá,

talvez esteja faltando alguma coisa, mas quando uso essa função e redireciono para outra rolagem de página não é redefinida para o topo, mas a página é carregada rolada para o meio, eu sei por que esse problema acontece, mas posso corrigi-lo de alguma forma com algum sinalizador como JAVASCRIPT Router.pusht({pathname: '/link', scrollreset: true}) ou algo assim, não encontrei nada parecido na documentação

Comentários muito úteis

Concordo que deveria haver uma opção scrollreset . Se você não quiser fazer isso em componentDidMount você também pode fazer Router.push('/link').then(() => window.scrollTo(0, 0)); .

Todos 25 comentários

se deve rolar para o topo ou não é decidido pelo usuário. Eu acho que você pode fazer isso em componentDidMount

Concordo que deveria haver uma opção scrollreset . Se você não quiser fazer isso em componentDidMount você também pode fazer Router.push('/link').then(() => window.scrollTo(0, 0)); .

Router.push('/link').then(() => window.scrollTo(0, 0)); este é o jeito certo de fazer isso, sinta-se à vontade para adicioná-lo ao readme @gragland ❤️

Obrigado @gralagland sua solução não parece tão feia, mas seria bom ter essa opção para manter o DRY principe. A única solução rápida por enquanto é fazer um modelo ao vivo para escrever isso 😅

Uau... Você quer dizer que eu preciso revisar cada página para adicionar .then() ! A propósito, estou usando next-routes , então existe uma maneira de adicionar isso globalmente, apenas para adicionar esse código uma vez, como addRequestTransform se você precisar adicionar alguns cabeçalhos antes de chamar uma API.

@MBach você também pode ligar window.scrollTo(0, 0) em componentDidMount do componente traget.

Eu só quero entrar na conversa e dizer que achei esse comportamento extremamente surpreendente. Estou construindo sites de produção com Next.js há quase um ano e só agora descobri que Router.push não estava voltando ao topo – eu sempre pensei que Router.push se comportasse exatamente como <Link> , mas era apenas a maneira programática de fazer isso.

E em termos de comportamento padrão correspondente como <a> vs. <Link> , o equivalente não-SPA de Router.push parece estar definindo window.location que também retorna você para o topo da página, não? Faria mais sentido como o comportamento padrão para mim.

@gragland não funciona

Para fazê-lo funcionar globalmente, você pode usar o ouvinte de eventos do roteador next.js integrado: Router.events.on('routeChangeComplete', () => { window.scrollTo(0, 0); }); .
Basta colocar isso em um componente compartilhado em todas as páginas, por exemplo, o cabeçalho.

@timneutkens Existe uma razão pela qual o comportamento de rolagem padrão para o imperativo Router.push e <Link> são diferentes? Parece um bug óbvio para mim que eles se comportam de maneira diferente.

Router.back() não retorna promessa, mas tem o mesmo problema, o que fazer, rolando manualmente para cima?

@alexsenichev Você tentou a solução @macmenak ?

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

Oi. Por que teríamos o componente padrão Link rolando para o topo true mas Router.push não?

Acho que esse assunto deveria ser revisto.

Desculpe, mas marcando os contribuidores: @liweinan0423 @timneutkens @exogen @goldenshun

Seria bom se houvesse uma opção em router.push() para forçar a rolagem para o topo, mas não acho que deveria ser o padrão, pois seria uma mudança importante para todos já assumindo que não faz isso.

@ markjackson02 Não vejo como isso seria uma mudança importante, pois essa é a maneira padrão como o navegador lidaria com isso. Não concordo com o fato de termos duas abordagens diferentes para a mesma funcionalidade.

Seria definitivamente uma mudança de ruptura. As pessoas estão construindo aplicativos do jeito que são agora e esperam que funcionem dessa maneira.

Não sou contra que ambos tenham a mesma funcionalidade padrão, mas não acho que valha a pena o esforço de migração.

De qualquer forma, esta questão está encerrada, então devemos abrir uma nova questão com o que quisermos fazer.

@markjackson02 OMHO, a mudança principal é que ainda não está funcionando dessa maneira! O comportamento errado já foi feito. Então eu acho que seria uma boa ideia colocá-lo da maneira que deveria. Mas concordo com você, os contribuidores provavelmente não darão atenção aqui.

@Timer @timneutkens Além disso, fui mordido pela diferença de comportamento de Link e Router.push . FWIW, +1 para adicionar options de Router.push , pois isso o manteria compatível com versões anteriores.

Você pode usar isso para rotear e rolar até o topo da 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},
})

Aliás, essa não é uma experiência de usuário tão boa quanto o comportamento Link , já que você rola para o topo da página imediatamente, em vez de quando a próxima página é carregada.

Adicionei um problema relacionado aqui para os interessados: https://github.com/vercel/next.js/issues/15206

FWIW, aqui está minha opinião sobre uma solução alternativa que foi compartilhada em um 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;

Use-o em algum lugar no topo da sua árvore 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 quando esses campos mudam? behavior, left, top . Para trás e para a frente não funciona para mim.

@rakeshshubhu Isso afeta apenas a navegação por next/router .

Uau... Você quer dizer que eu preciso revisar cada página para adicionar .then() ! A propósito, estou usando next-routes , então existe uma maneira de adicionar isso globalmente, apenas para adicionar esse código uma vez, como addRequestTransform se você precisar adicionar alguns cabeçalhos antes de chamar uma API.

Você não precisa. Em vez disso, crio um módulo e o exporto para os componentes que precisam dele. Então, uma vez que precisa mudar, você muda esse único lugar.

Além disso, você percebe que estou passando o roteador como parâmetro, pois você só pode usar React Hooks, por exemplo, useRouter() dentro de um componente

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

  // Force scrolling to top
  router.push(url).then(() => window.scrollTo(0, 0));
};
Esta página foi útil?
0 / 5 - 0 avaliações