Next.js: Router.push('/link') scrollt nicht nach oben auf der Seite, wenn es ausgelöst wird

Erstellt am 7. Nov. 2017  ·  25Kommentare  ·  Quelle: vercel/next.js

Hallo,

Vielleicht fehlt mir etwas, aber wenn ich diese Funktion verwende und auf eine andere Seite umleite, wird das Scrollen nicht nach oben zurückgesetzt, sondern die Seite wird nach Mitte gescrollt. Ich weiß, warum dieses Problem auftritt, aber kann ich es irgendwie mit einem Flag wie JAVASCRIPT Router.pusht({pathname: '/link', scrollreset: true}) beheben

Hilfreichster Kommentar

Ich stimme zu, dass es eine scrollreset -Option geben sollte. Wenn Sie es nicht in componentDidMount machen wollen, können Sie auch Router.push('/link').then(() => window.scrollTo(0, 0)); machen.

Alle 25 Kommentare

ob nach oben gescrollt werden soll oder nicht, entscheidet der Nutzer. Ich denke, Sie können es in componentDidMount tun

Ich stimme zu, dass es eine scrollreset -Option geben sollte. Wenn Sie es nicht in componentDidMount machen wollen, können Sie auch Router.push('/link').then(() => window.scrollTo(0, 0)); machen.

Router.push('/link').then(() => window.scrollTo(0, 0)); das ist der richtige Weg, füge es gerne der Readme @gragland ❤️ hinzu

Danke @gralagland , deine Lösung scheint nicht so hässlich zu sein, aber es wäre schön, eine solche Option zu haben, um das DRY-Prinzip beizubehalten. Die einzige schnelle Lösung ist im Moment, eine Live-Vorlage zu erstellen, um dies zu schreiben 😅

Wow... Du meinst, ich muss jede einzelne Seite überprüfen, um .then() hinzuzufügen! Übrigens verwende ich next-routes . Gibt es also eine Möglichkeit, dies global hinzuzufügen, indem Sie diesen Code nur einmal hinzufügen, z. B. addRequestTransform , wenn Sie einige Header hinzufügen müssen, bevor Sie eine API aufrufen.

@Mbach Sie können auch window.scrollTo(0, 0) auf componentDidMount der Traget-Komponente einhaken.

Ich möchte mich nur einmischen und sagen, dass ich dieses Verhalten äußerst überraschend fand. Ich baue jetzt seit fast einem Jahr Produktionsseiten mit Next.js und habe erst jetzt festgestellt, dass Router.push nicht an die Spitze zurückkehrt – ich dachte immer, Router.push verhält sich genauso wie <Link> aber war nur der programmatische Weg, dies zu tun.

Und in Bezug auf das Abgleichen von Standardverhalten wie <a> vs. <Link> scheint das Nicht-SPA-Äquivalent von Router.push window.location , was Sie ebenfalls zurückgibt nach oben auf der Seite, nein? Als Standardverhalten wäre es für mich sinnvoller.

@gragland es funktioniert nicht

Damit es global funktioniert, können Sie den integrierten next.js-Router-Ereignis-Listener verwenden: Router.events.on('routeChangeComplete', () => { window.scrollTo(0, 0); }); .
Platzieren Sie dies einfach in einer Komponente, die von allen Seiten gemeinsam genutzt wird, z. B. die Kopfzeile.

@timneutkens Gibt es einen Grund, warum das Standard-Bildlaufverhalten für den Imperativ Router.push und <Link> unterschiedlich ist? Scheint mir ein offensichtlicher Fehler zu sein, dass sie sich anders verhalten.

Router.back() gibt kein Versprechen zurück, hat aber das gleiche Problem, was tun, manuell nach oben scrollen?

@alexsenichev Hast du die Lösung von @macmenak ausprobiert?

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

Hallo. Warum sollte die Komponente Link standardmäßig nach oben scrollen, true , aber Router.push nicht?

Ich denke, dieses Thema sollte noch einmal aufgegriffen werden.

Tut mir leid, aber die Mitwirkenden zu markieren: @liweinan0423 @timneutkens @exogen @goldenshun

Es wäre schön, wenn es eine Option für router.push() gäbe, um das Scrollen nach oben zu erzwingen, aber ich denke nicht, dass dies die Standardeinstellung sein sollte, da es eine bahnbrechende Änderung für alle wäre, die bereits davon ausgehen, dass dies nicht der Fall ist.

@ markjackson02 Ich sehe nicht, wie dies eine Breaking Change wäre, da dies die Standardmethode ist, mit der der Browser damit umgehen würde. Ich bin nicht damit einverstanden, dass wir zwei unterschiedliche Ansätze für die gleiche Funktionalität haben.

Es wäre auf jeden Fall ein Breaking Change. Die Leute haben Apps so entwickelt, wie sie jetzt sind, und erwarten, dass sie so funktionieren.

Ich bin nicht dagegen, dass beide die gleiche Standardfunktionalität haben, aber ich denke nicht, dass es den Migrationsaufwand wert ist.

Wie auch immer, dieses Thema ist geschlossen, also sollten wir ein neues Thema mit dem eröffnen, was wir tun möchten.

@markjackson02 OMHO, die bahnbrechende Änderung ist, dass es noch nicht so funktioniert! Das falsche Verhalten ist bereits gemacht. Also ich denke, es wäre eine gute Idee, es so zu formulieren, wie es sollte. Aber ich stimme dir zu, Mitwirkende werden hier wohl nicht aufpassen.

@Timer @timneutkens Außerdem wurde ich von dem unterschiedlichen Verhalten von Link und Router.push gebissen. FWIW, +1 zum Hinzufügen von Router.push zu den options $, da dies abwärtskompatibel bleiben würde.

Sie können dies verwenden, um zum Anfang der Seite zu navigieren und zu scrollen:

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

Übrigens ist dies nicht so angenehm für die Benutzererfahrung wie das Link -Verhalten, da Sie sofort zum Anfang der Seite scrollen, anstatt wenn die nächste Seite geladen wird.

Für Interessierte habe ich hier ein verwandtes Problem hinzugefügt: https://github.com/vercel/next.js/issues/15206

FWIW, hier ist meine Meinung zu einer Problemumgehung, die in einem verwandten Problem geteilt wurde.

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;

Verwenden Sie es irgendwo oben in Ihrem Komponentenbaum ( _app.js funktioniert).

// _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 wann ändern sich diese Felder? behavior, left, top . Vor und zurück funktioniert bei mir nicht.

@rakeshshubhu Dies betrifft nur die Navigation durch next/router .

Wow... Du meinst, ich muss jede einzelne Seite überprüfen, um .then() hinzuzufügen! Übrigens verwende ich next-routes . Gibt es also eine Möglichkeit, dies global hinzuzufügen, indem Sie diesen Code nur einmal hinzufügen, z. B. addRequestTransform , wenn Sie einige Header hinzufügen müssen, bevor Sie eine API aufrufen.

Das müssen Sie nicht. Stattdessen erstelle ich ein Modul und exportiere es in die Komponenten, die es benötigen. Sobald es also geändert werden muss, ändern Sie diesen einen einzigen Ort.

Außerdem stellen Sie fest, dass ich den Router als Parameter übergebe, da Sie React Hooks, z. B. useRouter(), nur innerhalb einer Komponente verwenden können

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

  // Force scrolling to top
  router.push(url).then(() => window.scrollTo(0, 0));
};
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen