web-dev-qa-db-fra.com

réagir-routeur défiler vers le haut à chaque transition

J'ai un problème lorsque je navigue dans une autre page, sa position restera identique à la page précédente. Donc, il ne fera pas défiler vers le haut automatiquement. J'ai également essayé d'utiliser window.scrollTo(0, 0) sur un routeur onChange. J'ai également utilisé scrollBehavior pour résoudre ce problème mais cela n'a pas fonctionné. Une suggestion à ce sujet?

52
adrian hartanto

La documentation de React Router v4 contient exemples de code pour la restauration du défilement. Voici leur premier exemple de code, qui sert de solution à l’échelle du site pour "faire défiler vers le haut. ”Quand une page est accédée à:

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)

Rendez-le ensuite en haut de votre application, mais en dessous de Router:

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

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

^ Copié directement de la documentation

Évidemment, cela fonctionne dans la plupart des cas, mais il y a plus d'informations sur la manière de traiter les interfaces à onglets et sur la raison pour laquelle une solution générique n'a pas été mise en œuvre.

69
mtl

Cette réponse concerne le code hérité, le routeur v4 + vérifie les autres réponses

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

Si cela ne fonctionne pas, vous devriez trouver la raison. Aussi à l'intérieur componentDidMount

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

vous pouvez utiliser:

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

vous pouvez ajouter un drapeau comme "scrolled = false" puis dans update:

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

Pour react-router v4 , voici une application create-react-application permettant de restaurer le défilement: http: // router-scroll- top.surge.sh/ .

Pour ce faire, vous pouvez créer un décor pour le composant Route et tirer parti des méthodes de cycle de vie:

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);

Sur le componentDidUpdate, nous pouvons vérifier quand le chemin d'accès à l'emplacement change et le faire correspondre à pathprop et, si ceux-ci sont satisfaits, restaurer le défilement de la fenêtre.

Ce qui est bien avec cette approche, c'est que nous pouvons avoir des itinéraires qui restaurent le défilement et des itinéraires qui ne restaurent pas le défilement.

Voici un App.js exemple d'utilisation de ce qui précède:

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;

À partir du code ci-dessus, il est intéressant de souligner que ce n'est qu'en naviguant vers /about ou /another-page l'action défiler vers le haut sera préformée. Cependant quand on passe / aucune restauration de défilement ne se produira.

La base de code entière peut être trouvée ici: https://github.com/rizedr/react-router-scroll-top

21
Dragos Rizescu

mais les classes sont tellement 2018

Mise en oeuvre ScrollToTop avec les crochets React

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);

Usage:

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

ScrollToTop peut également être implémenté en tant que composant wrapper:

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);

Usage:

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

Il est à noter que la méthode onUpdate={() => window.scrollTo(0, 0)} est obsolète.

Voici une solution simple pour react-router 4+.

const history = createBrowserHistory()

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

<Router history={history}>
11
Harry Le

J'ai eu le même problème avec mon application.L'utilisation de l'extrait de code ci-dessous m'a aidée à faire défiler le haut de la page en cliquant sur le bouton suivant.

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

Cependant, le problème persistait au retour du navigateur. Après beaucoup d'essais, j'ai réalisé que c'était à cause de l'objet d'historique de la fenêtre du navigateur, qui avait une propriété scrollRestoration qui avait la valeur auto. Ce paramètre réglé à manuel a résolu mon problème.

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

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

J'ai écrit un composant d'ordre supérieur appelé withScrollToTop. Ce COH prend deux drapeaux:

  • onComponentWillMount - Faut-il faire défiler vers le haut lors de la navigation (componentWillMount)
  • onComponentDidUpdate - S'il faut faire défiler vers le haut lors de la mise à jour (componentDidUpdate). Cet indicateur est nécessaire dans les cas où le composant n'est pas démonté mais qu'un événement de navigation se produit, par exemple de /users/1 à /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));
};

Pour l'utiliser:

import withScrollToTop from './withScrollToTop';

function MyComponent() { ... }

export default withScrollToTop(MyComponent);
2
Yangshun Tay

Voici une autre méthode.

Pour react-router v4, vous pouvez également lier un écouteur pour modifier l'événement de l'historique, de la manière suivante:

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'));

Le niveau de défilement sera mis à 0 sans setImmediate() aussi si la route est modifiée en cliquant sur un lien, mais si l'utilisateur appuie sur le bouton précédent du navigateur, il ne fonctionnera pas en tant que navigateur réinitialisant manuellement le niveau de défilement au précédent. level lorsque le bouton de retour est enfoncé. Ainsi, en utilisant setImmediate(), notre fonction est exécutée une fois que le navigateur a terminé de réinitialiser le niveau de défilement, nous donnant ainsi l'effet souhaité.

1
Zeus

avec React routeur v4, vous pouvez utiliser

créer un composant scrollToTopComponent comme celui ci-dessous

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)

ou si vous utilisez des onglets utilisez quelque chose comme celui ci-dessous

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

espérons que cela aide pour plus sur la restauration de défilement vist il docs lièvre réaction routeur de défilement dom

1
user2907964

Pour les applications plus petites, avec 1 à 4 itinéraires, vous pouvez essayer de le pirater avec une redirection vers l'élément DOM supérieur avec #id au lieu d'un itinéraire. Dans ce cas, il n'est pas nécessaire d'encapsuler les itinéraires dans ScrollToTop ou à l'aide de méthodes de cycle de vie.

0
yakato