webentwicklung-frage-antwort-db.com.de

React-Router Bei jedem Übergang nach oben scrollen

Beim Navigieren auf eine andere Seite ist ein Problem aufgetreten. Die Position bleibt wie auf der vorherigen Seite. Es wird also nicht automatisch nach oben gescrollt. Ich habe auch versucht, window.scrollTo(0, 0) auf onChange-Router zu verwenden. Ich habe auch scrollBehavior verwendet, um dieses Problem zu beheben, aber es hat nicht funktioniert. Irgendwelche Vorschläge dazu?

52
adrian hartanto

Die Dokumentation zu React Router v4 enthält Codebeispiele für die Bildlaufwiederherstellung. Hier ist das erste Codebeispiel, das als standortweite Lösung für "Bildlauf nach oben" dient ”Wenn eine Seite navigiert wird zu:

class ScrollToTop extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.location !== prevProps.location) {
      window.scrollTo(0, 0)
    }
  }

  render() {
    return this.props.children
  }
}

export default withRouter(ScrollToTop)

Dann rendere es oben in deiner App, aber unter dem Router:

const App = () => (
  <Router>
    <ScrollToTop>
      <App/>
    </ScrollToTop>
  </Router>
)

// or just render it bare anywhere you want, but just one :)
<ScrollToTop/>

^ direkt aus der Dokumentation übernommen

Natürlich funktioniert dies in den meisten Fällen, aber es gibt weitere Informationen zum Umgang mit Registerkarten und warum keine generische Lösung implementiert wurde.

69
mtl

Diese Antwort gilt für Legacy-Code, für Router v4 + andere Antworten prüfen

<Router onUpdate={() => window.scrollTo(0, 0)} history={createBrowserHistory()}>
  ...
</Router>

Wenn es nicht funktioniert, sollten Sie den Grund finden. Auch in componentDidMount

document.body.scrollTop = 0;
// or
window.scrollTo(0,0);

du könntest benutzen:

componentDidUpdate() {
  window.scrollTo(0,0);
}

sie könnten ein Flag wie "scrolled = false" und dann in update hinzufügen:

componentDidUpdate() {
  if(this.scrolled === false){
    window.scrollTo(0,0);
    scrolled = true;
  }
}
26
Lukas

Für react-router v4 ist hier eine create-react-App, die die Scrollwiederherstellung durchführt: http: // router-scroll- top.surge.sh/ .

Um dies zu erreichen, können Sie die Komponente Route dekorieren und die Lebenszyklusmethoden nutzen:

import React, { Component } from 'react';
import { Route, withRouter } from 'react-router-dom';

class ScrollToTopRoute extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.path === this.props.location.pathname && this.props.location.pathname !== prevProps.location.pathname) {
      window.scrollTo(0, 0)
    }
  }

  render() {
    const { component: Component, ...rest } = this.props;

    return <Route {...rest} render={props => (<Component {...props} />)} />;
  }
}

export default withRouter(ScrollToTopRoute);

Auf dem componentDidUpdate können wir überprüfen, wann sich der Pfadname des Standorts ändert, und ihn mit der Requisite path abgleichen und, wenn diese zufrieden sind, den Bildlauf des Fensters wiederherstellen.

Das Coole an diesem Ansatz ist, dass es Routen geben kann, die den Bildlauf wiederherstellen, und Routen, die den Bildlauf nicht wiederherstellen.

Hier ist ein App.js Beispiel, wie Sie das oben genannte verwenden können:

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Lorem from 'react-lorem-component';
import ScrollToTopRoute from './ScrollToTopRoute';
import './App.css';

const Home = () => (
  <div className="App-page">
    <h2>Home</h2>
    <Lorem count={12} seed={12} />
  </div>
);

const About = () => (
  <div className="App-page">
    <h2>About</h2>
    <Lorem count={30} seed={4} />
  </div>
);

const AnotherPage = () => (
  <div className="App-page">
    <h2>This is just Another Page</h2>
    <Lorem count={12} seed={45} />
  </div>
);

class App extends Component {
  render() {
    return (
      <Router>
        <div className="App">
          <div className="App-header">
            <ul className="App-nav">
              <li><Link to="/">Home</Link></li>
              <li><Link to="/about">About</Link></li>
              <li><Link to="/another-page">Another Page</Link></li>
            </ul>
          </div>
          <Route exact path="/" component={Home} />
          <ScrollToTopRoute path="/about" component={About} />
          <ScrollToTopRoute path="/another-page" component={AnotherPage} />
        </div>
      </Router>
    );
  }
}

export default App;

In Bezug auf den obigen Code ist es interessant, darauf hinzuweisen, dass nur bei der Navigation zu /about oder /another-page Die Aktion "Nach oben scrollen" wird ausgeführt. Aber wenn es weitergeht / Es wird keine Bildlaufwiederherstellung durchgeführt.

Die gesamte Codebasis finden Sie hier: https://github.com/rizedr/react-router-scroll-top

21
Dragos Rizescu

aber Klassen sind so 2018

ScrollToTop-Implementierung mit React Hooks

ScrollToTop.js

import { useEffect } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return (null);
}

export default withRouter(ScrollToTop);

Verwendung:

<Router>
  <Fragment>
    <ScrollToTop />
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </Fragment>
</Router>

ScrollToTop kann auch als Wrapper-Komponente implementiert werden:

ScrollToTop.js

import React, { useEffect, Fragment } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history, children }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return <Fragment>{children}</Fragment>;
}

export default withRouter(ScrollToTop);

Verwendung:

<Router>
  <ScrollToTop>
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </ScrollToTop>
</Router>
19
zurfyx

Es ist bemerkenswert, dass die Methode onUpdate={() => window.scrollTo(0, 0)} veraltet ist.

Hier ist eine einfache Lösung für React-Router 4+.

const history = createBrowserHistory()

history.listen(_ => {
    window.scrollTo(0, 0)  
})

<Router history={history}>
11
Harry Le

Ich hatte das gleiche Problem mit meiner Anwendung. Mit dem folgenden Codeausschnitt konnte ich beim Klicken auf die nächste Schaltfläche zum Seitenanfang scrollen.

<Router onUpdate={() => window.scrollTo(0, 0)} history= {browserHistory}>
...
</Router>

Das Problem trat jedoch weiterhin im Browser auf. Nach vielen Versuchen wurde mir klar, dass dies an dem Verlaufsobjekt des Browserfensters lag, dessen Eigenschaft scrollRestoration auf auto gesetzt war. Das Setzen auf manual löste mein Problem.

function scrollToTop() {
    window.scrollTo(0, 0)
    if ('scrollRestoration' in history) {
        history.scrollRestoration = 'manual';
    }
}

<Router onUpdate= {scrollToTop} history={browserHistory}>
....
</Router>
7
Jhalaa Chinoy

Ich habe eine übergeordnete Komponente namens withScrollToTop geschrieben. Dieser HOC enthält zwei Flags:

  • onComponentWillMount - Gibt an, ob bei der Navigation nach oben gescrollt werden soll (componentWillMount)
  • onComponentDidUpdate - Gibt an, ob beim Update ein Bildlauf nach oben ausgeführt werden soll (componentDidUpdate). Dieses Flag ist in Fällen erforderlich, in denen die Komponente nicht abgehängt ist, aber ein Navigationsereignis auftritt, z. B. von /users/1 bis /users/2.

// @flow
import type { Location } from 'react-router-dom';
import type { ComponentType } from 'react';

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';

type Props = {
  location: Location,
};

type Options = {
  onComponentWillMount?: boolean,
  onComponentDidUpdate?: boolean,
};

const defaultOptions: Options = {
  onComponentWillMount: true,
  onComponentDidUpdate: true,
};

function scrollToTop() {
  window.scrollTo(0, 0);
}

const withScrollToTop = (WrappedComponent: ComponentType, options: Options = defaultOptions) => {
  return class withScrollToTopComponent extends Component<Props> {
    props: Props;

    componentWillMount() {
      if (options.onComponentWillMount) {
        scrollToTop();
      }
    }

    componentDidUpdate(prevProps: Props) {
      if (options.onComponentDidUpdate &&
        this.props.location.pathname !== prevProps.location.pathname) {
        scrollToTop();
      }
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
};

export default (WrappedComponent: ComponentType, options?: Options) => {
  return withRouter(withScrollToTop(WrappedComponent, options));
};

Um es zu benutzen:

import withScrollToTop from './withScrollToTop';

function MyComponent() { ... }

export default withScrollToTop(MyComponent);
2
Yangshun Tay

Hier ist eine andere Methode.

Für react-router v4 können Sie einen Listener auch folgendermaßen an Änderungen im Verlaufsereignis binden:

let firstMount = true;
const App = (props) => {
    if (typeof window != 'undefined') { //incase you have server-side rendering too             
        firstMount && props.history.listen((location, action) => {
            setImmediate(() => window.scrollTo(0, 0)); // ive explained why i used setImmediate below
        });
        firstMount = false;
    }

    return (
        <div>
            <MyHeader/>            
            <Switch>                            
                <Route path='/' exact={true} component={IndexPage} />
                <Route path='/other' component={OtherPage} />
                // ...
             </Switch>                        
            <MyFooter/>
        </div>
    );
}

//mounting app:
render((<BrowserRouter><Route component={App} /></BrowserRouter>), document.getElementById('root'));

Die Bildlaufebene wird auch ohne setImmediate() auf 0 gesetzt, wenn die Route durch Klicken auf einen Link geändert wird. Wenn der Benutzer jedoch die Zurück-Taste im Browser drückt, funktioniert dies nicht, da der Browser die Bildlaufebene manuell auf die vorherige zurücksetzt Wenn die Zurück-Taste gedrückt wird, wird die Funktion mit setImmediate() ausgeführt, nachdem der Browser die Bildlaufebene zurückgesetzt hat, wodurch der gewünschte Effekt erzielt wird.

1
Zeus

mit React Router Dom v4 können Sie verwenden

erstellen Sie eine scrollToTopComponent-Komponente wie die folgende

class ScrollToTop extends Component {
    componentDidUpdate(prevProps) {
      if (this.props.location !== prevProps.location) {
        window.scrollTo(0, 0)
      }
    }

    render() {
      return this.props.children
    }
}

export default withRouter(ScrollToTop)

oder wenn Sie Tabs verwenden, verwenden Sie das Folgende

class ScrollToTopOnMount extends Component {
    componentDidMount() {
      window.scrollTo(0, 0)
    }

    render() {
      return null
    }
}

class LongContent extends Component {
    render() {
      <div>
         <ScrollToTopOnMount/>
         <h1>Here is my long content page</h1>
      </div>
    }
}

// somewhere else
<Route path="/long-content" component={LongContent}/>

ich hoffe, dies hilft mehr bei der Wiederherstellung von Bildläufen. Es gibt folgende Dokumente: hare react router dom scroll restoration

1
user2907964

Bei kleineren Apps mit 1-4 Routen können Sie versuchen, sie zu hacken, indem Sie stattdessen nur eine Route zum obersten DOM-Element mit #id umleiten. In diesem Fall müssen Sie keine Routen in ScrollToTop umbrechen oder Lebenszyklusmethoden verwenden.

0
yakato