Next.js: Router.push('/link') ne défile pas en haut de la page lorsqu'il est déclenché

Créé le 7 nov. 2017  ·  25Commentaires  ·  Source: vercel/next.js

Bonjour,

peut-être qu'il me manque quelque chose, mais lorsque j'utilise cette fonction et que je redirige vers une autre page, le défilement ne se réinitialise pas en haut mais la page se charge jusqu'au milieu, je sais pourquoi ce problème se produit, mais puis-je le résoudre d'une manière ou d'une autre avec un drapeau comme JAVASCRIPT Router.pusht({pathname: '/link', scrollreset: true}) ou quelque chose comme ça, n'a rien trouvé de similaire dans la documentation

Commentaire le plus utile

Je suis d'accord qu'il devrait y avoir une option scrollreset . Si vous ne voulez pas le faire en componentDidMount vous pouvez aussi faire Router.push('/link').then(() => window.scrollTo(0, 0)); .

Tous les 25 commentaires

si elle doit défiler vers le haut ou non est décidée par l'utilisateur. Je pense que vous pouvez le faire en componentDidMount

Je suis d'accord qu'il devrait y avoir une option scrollreset . Si vous ne voulez pas le faire en componentDidMount vous pouvez aussi faire Router.push('/link').then(() => window.scrollTo(0, 0)); .

Router.push('/link').then(() => window.scrollTo(0, 0)); c'est la bonne façon de le faire, n'hésitez pas à l'ajouter au readme @gragland ❤️

Merci @gralagland , votre solution ne semble pas si laide, mais ce serait bien d'avoir une telle option pour garder le principe DRY. La seule solution rapide pour l'instant est de créer un modèle en direct pour écrire ceci 😅

Woaw... Vous voulez dire que je dois revoir chaque page pour ajouter .then() ! Au fait, j'utilise next-routes , il existe donc un moyen d'ajouter ceci globalement, juste pour ajouter ce code une fois, comme addRequestTransform si vous devez ajouter des en-têtes avant d'appeler une API.

@MBach , vous pouvez également accrocher window.scrollTo(0, 0) sur componentDidMount du composant cible.

Je veux juste intervenir et dire que j'ai trouvé ce comportement extrêmement surprenant. Je construis des sites de production avec Next.js depuis près d'un an maintenant et je viens seulement de découvrir que Router.push ne revenait pas au sommet - j'ai toujours pensé que Router.push se comportait exactement comme <Link> mais c'était juste la façon programmatique de le faire.

Et en termes de comportement par défaut correspondant comme <a> vs <Link> , l'équivalent non-SPA de Router.push semblerait définir window.location qui vous renvoie également en haut de la page, non ? Cela aurait plus de sens comme comportement par défaut pour moi.

@gragland ça marche pas

Pour le faire fonctionner globalement, vous pouvez utiliser l'écouteur d'événement de routeur next.js intégré : Router.events.on('routeChangeComplete', () => { window.scrollTo(0, 0); }); .
Placez-le simplement dans un composant partagé sur toutes les pages, par exemple l'en-tête.

@timneutkens Y a-t-il une raison pour laquelle le comportement de défilement par défaut pour l'impératif Router.push et <Link> est différent? Cela me semble être un bug évident qu'ils se comportent différemment.

Router.back() ne renvoie pas de promesse, mais a le même problème, que faire, faire défiler manuellement vers le haut ?

@alexsenichev Avez-vous essayé la solution @macmenak ?

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

Salut. Pourquoi aurions-nous le défilement par défaut du composant Link vers le haut true mais pas Router.push ?

Je pense que cette question devrait être réexaminée.

Désolé mais taguer les contributeurs : @liweinan0423 @timneutkens @exogen @goldenshun

Ce serait bien s'il y avait une option sur router.push() pour forcer le défilement vers le haut, mais je ne pense pas que cela devrait être par défaut car ce serait un changement radical pour tout le monde en supposant déjà qu'il ne le fait pas.

@ markjackson02 Je ne vois pas en quoi ce serait un changement de rupture puisque c'est la façon par défaut dont le navigateur aurait géré cela. Je ne suis pas d'accord avec le fait que nous ayons deux approches différentes pour la même fonctionnalité.

Ce serait certainement un changement radical. Les gens ont créé des applications telles qu'elles sont aujourd'hui et s'attendent à ce qu'elles fonctionnent de cette façon.

Je ne suis pas contre le fait qu'ils aient tous les deux la même fonctionnalité par défaut, mais je ne pense pas que cela en vaille la peine.

Quoi qu'il en soit, ce problème est clos, nous devrions donc ouvrir un nouveau problème avec tout ce que nous voulons faire.

@markjackson02 OMHO, le changement majeur est que cela ne fonctionne pas encore de cette façon ! Le mauvais comportement est déjà fait. Je pense donc que ce serait une bonne idée de le présenter comme il se doit. Mais je suis d'accord avec vous, les contributeurs n'y prêteront probablement pas attention.

@Timer @timneutkens De plus, j'ai été mordu par la différence de comportement entre Link et Router.push . FWIW, +1 pour ajouter au options de Router.push car cela le garderait rétrocompatible.

Vous pouvez l'utiliser pour router et faire défiler vers le haut de la page :

// 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},
})

Au fait, ce n'est pas une expérience utilisateur aussi agréable que le comportement Link , puisque vous faites défiler immédiatement vers le haut de la page au lieu de charger la page suivante.

J'ai ajouté un problème connexe ici pour les personnes intéressées : https://github.com/vercel/next.js/issues/15206

FWIW, voici mon point de vue sur une solution de contournement qui a été partagée dans un problème connexe.

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;

Utilisez-le quelque part en haut de votre arborescence de composants ( _app.js fonctionne).

// _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 quand ces champs changent-ils ? behavior, left, top . En arrière et en avant ne fonctionne pas pour moi.

@rakeshshubhu Cela n'affecte que la navigation via next/router .

Woaw... Vous voulez dire que je dois revoir chaque page pour ajouter .then() ! Au fait, j'utilise next-routes , il existe donc un moyen d'ajouter ceci globalement, juste pour ajouter ce code une fois, comme addRequestTransform si vous devez ajouter des en-têtes avant d'appeler une API.

Vous n'êtes pas obligé. Je crée plutôt un module et l'exporte vers les composants qui en ont besoin. Donc, une fois qu'il faut changer, vous changez cet endroit unique.

Vous remarquez également que je passe le routeur en tant que paramètre puisque vous ne pouvez utiliser que React Hooks, par exemple useRouter() dans un composant

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

  // Force scrolling to top
  router.push(url).then(() => window.scrollTo(0, 0));
};
Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

sospedra picture sospedra  ·  3Commentaires

havefive picture havefive  ·  3Commentaires

wagerfield picture wagerfield  ·  3Commentaires

formula349 picture formula349  ·  3Commentaires

irrigator picture irrigator  ·  3Commentaires