Faites défiler vers le haut de la page après le rendu dans react.js

169

J'ai un problème, que je n'ai aucune idée, comment résoudre. Dans mon composant de réaction, j'affiche une longue liste de données et quelques liens en bas. Après avoir cliqué sur l'un de ces liens, je remplis la liste avec une nouvelle collection de liens et je dois faire défiler vers le haut.

Le problème est - comment faire défiler vers le haut une fois la nouvelle collection rendue?

'use strict';

// url of this component is #/:checklistId/:sectionId

var React = require('react'),
  Router = require('react-router'),
  sectionStore = require('./../stores/checklist-section-store');


function updateStateFromProps() {
  var self = this;
  sectionStore.getChecklistSectionContent({
    checklistId: this.getParams().checklistId,
    sectionId: this.getParams().sectionId
  }).then(function (section) {
    self.setState({
      section,
      componentReady: true
    });
  });

    this.setState({componentReady: false});
 }

var Checklist = React.createClass({
  mixins: [Router.State],

  componentWillMount: function () {
    updateStateFromProps.call(this);
  },

  componentWillReceiveProps(){
    updateStateFromProps.call(this);
   },

render: function () {
  if (this.state.componentReady) {
    return(
      <section className='checklist-section'>
        <header className='section-header'>{ this.state.section.name }   </header>
        <Steps steps={ this.state.section.steps }/>
        <a href=`#/${this.getParams().checklistId}/${this.state.section.nextSection.Id}`>
          Next Section
        </a>
      </section>
    );
    } else {...}
  }
});

module.exports = Checklist;
Andrew Kovalenko
la source

Réponses:

327

Enfin .. j'ai utilisé:

componentDidMount() {
  window.scrollTo(0, 0)
}

EDIT: React v16.8 +

useEffect(() => {
  window.scrollTo(0, 0)
}, [])
racémique
la source
2
C'est la seule solution qui a fonctionné pour moi. Également essayé: ReactDOM.findDOMNode (this) .scrollTop = 0 et componentDidMount () {this._div.scrollTop = 0} render () {return <div ref = {(ref) => this._div = ref} />}
Michael Bushe
Selon W3Schools, cette solution est actuellement prise en charge par tous les navigateurs. La bibliothèque ReactDOM est également obsolète dans les futures versions de React.
BishopZ
2
@Tomasz - J'ai trouvé que j'avais toujours ce problème parfois quand j'avais certains divs réglés sur la hauteur ou la hauteur min: 100%. J'ai dû l'enlever et l'envelopper dans un parent ou aller plus loin dans l'arbre où il pouvait encore défiler
racémique
2
Cela a fonctionné pour moi mais pas sous componentDidMount, car CDM peut ne pas être déclenché lorsque le changement d'état entraîne un re-rendu de la page. Alors mettez cet appel - window.scrollTo (0, 0); - où que vous soyez, vous changez d'état.
Puneet Lamba
4
Pour ceux qui utilisent des hooks, le code suivant fonctionnera. React.useEffect(() => { window.scrollTo(0, 0); }, []); Remarque, vous pouvez également importer directement useEffect:import { useEffect } from 'react'
Powderham
72

Étant donné que la solution d'origine a été fournie pour la toute première version de react , voici une mise à jour:

constructor(props) {
    super(props)
    this.myRef = React.createRef()   // Create a ref object 
}

componentDidMount() {
  this.myRef.current.scrollTo(0, 0);
}

render() {
    return <div ref={this.myRef}></div> 
}   // attach the ref property to a dom element
Andrew Kovalenko
la source
this.getDOMNode === undefined
Dave Lunny
1
@DaveLunny, vous êtes peut-être sur react15? essayez d'importer ReactDOM et faites ReactDOM.findDOMNode(this).scrollTop = 0
Marcus Ericsson
12
this is undefined in arrow functionsest incorrect. Le mot clé this est lié au même contexte que la fonction englobante developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Joe Delgado
Si possible, vous devez éviter ReactDom.findDOMNode (). Utilisez plutôt une référence. J'ai publié une solution en utilisant le défilement lisse ici
bbrinx
default.a.createRef n'est pas une fonction
Anupam Maurya
48

Vous pouvez utiliser quelque chose comme ça. ReactDom est pour react.14. Réagissez autrement.

    componentDidUpdate = () => { ReactDom.findDOMNode(this).scrollIntoView(); }

Mise à jour du 11/05/2019 pour React 16+

  constructor(props) {
    super(props)
    this.childDiv = React.createRef()
  }

  componentDidMount = () => this.handleScroll()

  componentDidUpdate = () => this.handleScroll()

  handleScroll = () => {
    const { index, selected } = this.props
    if (index === selected) {
      setTimeout(() => {
        this.childDiv.current.scrollIntoView({ behavior: 'smooth' })
      }, 500)
    }
  }

J. Mark Stevens
la source
De toutes les suggestions sur cette page, c'est la seule qui fonctionne pour moi.
Josh F
Remarque: si componentDidUpdate ne fonctionne pas pour vous, componentDidMountest une autre alternative.
Alex Fallenstedt
findDOMNode est une trappe d'échappement utilisée pour accéder au nœud DOM sous-jacent. Dans la plupart des cas, l'utilisation de cette trappe d'échappement est déconseillée car elle perce l'abstraction du composant. Il est obsolète dans StrictMode. reactjs.org/docs/react-dom.html
Vivek Kumar
16

Dans React Routing, il y a le problème que si nous redirigeons vers la nouvelle route, cela ne vous amènera pas automatiquement en haut de la page.

Même moi, j'ai eu le même problème.

Je viens d'ajouter la seule ligne à mon composant et cela a fonctionné comme du beurre.

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

Référer: formation de réaction

Vishal Shetty
la source
est-ce la méthode recommandée si j'utilise ceci pour mon bouton «sauter en haut»? ou s'il y a une manière de «réagir» où nous n'utilisons pas l'objet window?
Toxnyc
1
Merci d'avoir mis en garde, la solution que j'ai donnée est applicable pour une version react-router dom inférieure à la v5, j'utilisais la v4.2.2 et là, lorsque vous naviguez vers une autre page, vous n'avez pas été redirigé par défaut en haut de la page, nous devons donc amener manuellement l'utilisateur en haut de la page après la navigation, mais avec la v5.0.1 react-router dom a cessé d'envoyer la restauration de défilement hors de la boîte, car selon leur document, ils disent que les navigateurs ont commencé à prendre en charge cette fonctionnalité par défaut et avec la dernière version de react-router-dom, vous serez redirigé vers le haut de la page après la navigation.
Vishal Shetty
1
@Toxnyc donc utiliser un objet window est ce qu'est Javascript.Si react est au-dessus de Javascript, même si vous utilisez l'un des plugins React dans les coulisses, il utilisera uniquement Javascript et l'objet window, selon mes connaissances, le document react n'a pas tout ce qui nous permet d'obtenir les détails de l'écran de la fenêtre. nous devons utiliser Javascript pour que cela fonctionne.
Vishal Shetty
13

Cela pourrait, et devrait probablement, être géré à l'aide de références :

"... vous pouvez utiliser ReactDOM.findDOMNode comme une" trappe d'échappement "mais nous ne le recommandons pas car il rompt l'encapsulation et dans presque tous les cas, il existe une manière plus claire de structurer votre code dans le modèle React."

Exemple de code:

class MyComponent extends React.Component {
    componentDidMount() {
        this._div.scrollTop = 0
    }

    render() {
        return <div ref={(ref) => this._div = ref} />
    }
}
GGAlanSmithee
la source
Cela fonctionne très bien. Merci. Pour être clair, j'ai mis le <div ref={(ref) => this._div = ref} />dans le tout premier <div>de ma déclaration de rendu. Le reste de mon rendu reste exactement le même.
Josh F
Dans le cas où vous utilisez des composants Styled, vous devrez utiliser "innerRef" au lieu de "ref". Excellente solution
furcicm
Fonctionne totalement. Pour ce sur quoi je travaillais, je pourrais être encore plus simple avec <div ref="main">et ensuitethis.refs.main.scrollTop=0
chuckfactory
2
@chuckfactory définissant des références en utilisant des chaînes sera probablement supprimé à un moment donné, et présente en fait des inconvénients intéressants que vous voudrez peut-être connaître. news.ycombinator.com/edit?id=12093234
NJensen
10

Vous pouvez le faire dans le routeur comme ça:

ReactDOM.render((
<Router onUpdate={() => window.scrollTo(0, 0)} history={browserHistory}>
     <Route path='/' component={App}>
        <IndexRoute component={Home}></IndexRoute>
        <Route path="/about" component={About}/>
        <Route path="/work">
            <IndexRoute component={Work}></IndexRoute>
            <Route path=":id" component={ProjectFull}></Route>
        </Route>
        <Route path="/blog" component={Blog}/>
    </Route>
 </Router>
), document.getElementById('root'));

Le onUpdate={() => window.scrollTo(0, 0)}mettre le haut de défilement. Pour plus d'informations, consultez: lien codepen

Ana Maria Cabrera
la source
solution élégante qui ne nécessite qu'un petit changement de code dans le routeur plutôt que de laisser chaque composant le gérer lui-même. <3
alengel
Malheureusement, onUpdate se déclenche à chaque nouvelle routeParam acheminée dans une route donnée. Donc, si vous avez par exemple une page avec un tas d'images, et si vous pouvez agrandir l'image dans un modal lorsque vous cliquez sur en changeant la route vers /somePage/:imgId, elle défilera vers le haut: (. N'importe quelle manière de "contrôler" le déclenchement ou non l'événement onUpdate sur des routes / paramètres spécifiques?
connected_user
Quand j'ai essayé cela, TypeScript s'est plaint qu'il onUpdaten'existe pas dans les accessoires de HashRouter ... Si quelqu'un rencontre le même problème: j'ai fini par utiliser la solution ScrollToTop décrite plus bas (et dans la documentation de react-router) qui fonctionnait parfaitement pour moi.
Nicole
9

Pour ceux qui utilisent des hooks, le code suivant fonctionnera.

React.useEffect(() => {
  window.scrollTo(0, 0);
}, []);

Remarque, vous pouvez également importer directement useEffect: import { useEffect } from 'react'

Powderham
la source
1
Le []deuxième paramètre signifie que cela ne se produira que sur le premier rendu, avez-vous essayé sans?
Powderham
8

Voici encore une autre approche qui vous permet de choisir les composants montés sur lesquels vous souhaitez réinitialiser la position de défilement de la fenêtre sans dupliquer en masse le ComponentDidUpdate / ComponentDidMount.

L'exemple ci-dessous encapsule le composant Blog avec ScrollIntoView (), de sorte que si l'itinéraire change lorsque le composant Blog est monté, le ComponentDidUpdate du HOC met à jour la position de défilement de la fenêtre.

Vous pouvez tout aussi facilement l'envelopper sur l'ensemble de l'application, de sorte que lors de tout changement d'itinéraire, il déclenchera une réinitialisation de la fenêtre.

ScrollIntoView.js

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

export default WrappedComponent => {
  class ResetWindowScroll extends Component {
    componentDidUpdate = (prevProps) => {
      if(this.props.location !== prevProps.location) window.scrollTo(0,0);
    }

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

Routes.js

import React from 'react';
import { Route, IndexRoute } from 'react-router';

import App from '../components/App';
import About from '../components/pages/About';
import Blog from '../components/pages/Blog'
import Index from '../components/Landing';
import NotFound from '../components/navigation/NotFound';
import ScrollIntoView from '../components/navigation/ScrollIntoView';

 export default (
    <Route path="/" component={App}>
        <IndexRoute component={Index} />
        <Route path="/about" component={About} /> 
        <Route path="/blog" component={ScrollIntoView(Blog)} />
        <Route path="*" component={NotFound} />
    </Route>
);

L'exemple ci-dessus fonctionne très bien, mais si vous avez migré vers react-router-dom, vous pouvez simplifier ce qui précède en créant un HOCqui encapsule le composant.

Encore une fois, vous pouvez également l'envelopper tout aussi facilement sur vos routes (il suffit de changer la componentDidMountméthode en l' componentDidUpdateexemple de code de méthode écrit ci-dessus, ainsi que de l'envelopper ScrollIntoViewavec withRouter).

conteneurs / ScrollIntoView.js

import { PureComponent, Fragment } from "react";

class ScrollIntoView extends PureComponent {
  componentDidMount = () => window.scrollTo(0, 0);

  render = () => this.props.children
}

export default ScrollIntoView;

composants / Home.js

import React from "react";
import ScrollIntoView from "../containers/ScrollIntoView";

export default () => (
  <ScrollIntoView>
    <div className="container">
      <p>
        Sample Text
      </p>
    </div>
  </ScrollIntoView>
);
Matt Carlotta
la source
ScrollIntoView.js me donne l'erreur suivante "expression inutilisée, attend une affectation ou un appel de fonction"
EX0MAK3R
@ EX0MAK3R - Réponse mise à jour.
Matt Carlotta
7

C'est la seule chose qui a fonctionné pour moi (avec un composant de classe ES6):

componentDidMount() {
  ReactDOM.findDOMNode(this).scrollIntoView();
}
danharsanyi
la source
Également. J'ai essayé toutes les autres solutions et c'est la seule qui a fonctionné pour moi.
Erik James Robles
7

J'utilise le composant ScrollToTop de react-router dont le code est décrit dans la documentation de react-router

https://reacttraining.com/react-router/web/guides/scroll-restoration/scroll-to-top

Je change de code dans un seul fichier Routes et après cela, je n'ai pas besoin de changer de code dans chaque composant.

Exemple de code -

Étape 1 - Créez le composant ScrollToTop.js

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

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)

Étape 2 - Dans le fichier App.js, ajoutez le composant ScrollToTop après <Router

const App = () => (
  <Router>
    <ScrollToTop>
      <App/>
    </ScrollToTop>
  </Router>
)
Arpit
la source
vraiment bonne solution! si vous avez des routes, affichez-les simplement en haut de vos routes, mais en dessous de Router.Je n'ai pas eu à changer chaque composant.
éruption cutanée le
5

Solution de crochet :

  • Créer un hook ScrollToTop

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

    const ScrollToTop = ({ children, location: { pathname } }) => {
      useEffect(() => {
        window.scrollTo({
          top: 0,
          left: 0,
          behavior: "smooth"
        });
      }, [pathname]);

      return children || null;
    };

    export default withRouter(ScrollToTop);
  • Emballez votre application avec

    <Router>
        <ScrollToTop>
           <App />
        </ScrollToTop>
    </Router>

Documentation: https://reacttraining.com/react-router/web/guides/scroll-restoration

Quentin C
la source
5

Utilisation de crochets dans les composants fonctionnels, en supposant que le composant se met à jour lorsqu'il y a une mise à jour dans les accessoires de résultat

import React, { useEffect } from 'react';

export const scrollTop = ({result}) => {
  useEffect(() => {
    window.scrollTo(0, 0);
  }, [result])
}
Gabriel Ezenwankwo
la source
fr.reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook N'oubliez pas, un nom de hook doit commencer par le motuse
CrsCaballero
4

Tout ce qui précède n'a pas fonctionné pour moi - je ne sais pas pourquoi mais:

componentDidMount(){
    document.getElementById('HEADER').scrollIntoView();
}

travaillé, où HEADER est l'identifiant de mon élément d'en-tête

James Shiztar
la source
J'ai utilisé un hook useEffect mais cela a très bien fonctionné pour moi sur un projet Gatsby. Merci!
jj0b
4

Si tout le monde veut faire quelque chose de simple, voici une solution qui fonctionnera pour tout le monde

ajouter cette mini fonction

scrollTop()
{
    window.scrollTo({
        top: 0,
        behavior: "smooth"
    });
}

appeler la fonction comme suit depuis le pied de page de la page

<a className="scroll-to-top rounded" style={{display: "inline"}} onClick={this.scrollTop}>TOP</a>

si vous voulez ajouter de jolis styles, voici le css

.scroll-to-top {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  display: none;
  width: 2.75rem;
  height: 2.75rem;
  text-align: center;
  color: #fff;
  background: rgba(90, 92, 105, 0.5);
  line-height: 46px;
}

jerryurenaa
la source
l'extrait de code ne semble pas fonctionner. Mais la solution a fonctionné pour moi. Merci et bravo!
globefire
3

J'utilise React Hooks et je voulais quelque chose de réutilisable mais aussi quelque chose que je pourrais appeler à tout moment (plutôt que juste après le rendu).

// utils.js
export const useScrollToTop = (initialScrollState = false) => {
  const [scrollToTop, setScrollToTop] = useState(initialScrollState);

  useEffect(() => {
    if (scrollToTop) {
      setScrollToTop(false);
      try {
        window.scroll({
          top: 0,
          left: 0,
          behavior: 'smooth',
        });
      } catch (error) {
        window.scrollTo(0, 0);
      }
    }
  }, [scrollToTop, setScrollToTop]);

  return setScrollToTop;
};

Ensuite, pour utiliser le crochet, vous pouvez faire:

import { useScrollToTop } from 'utils';

const MyPage = (props) => {
  // initialise useScrollToTop with true in order to scroll on page load 
  const setScrollToTop = useScrollToTop(true);

  ...

  return <div onClick={() => setScrollToTop(true)}>click me to scroll to top</div>
}
GavKilbride
la source
Excellente solution!
Nick le
2

Si vous faites cela pour mobile , au moins avec chrome, vous verrez une barre blanche en bas.

Cela se produit lorsque la barre d'URL disparaît. Solution:

Modifiez le css pour height / min-height: 100% en height / min-height: 100vh .

Google Developer Docs

Artur Carvalho
la source
1

Aucune des réponses ci-dessus ne fonctionne actuellement pour moi. Il s'avère que ce .scrollTon'est pas aussi largement compatible que .scrollIntoView.

Dans notre App.js, dans componentWillMount()nous avons ajouté

this.props.history.listen((location, action) => {
        setTimeout(() => { document.getElementById('root').scrollIntoView({ behavior: "smooth" }) }, 777)
    })

C'est la seule solution qui fonctionne universellement pour nous. root est l'ID de notre application. Le comportement «fluide» ne fonctionne pas sur tous les navigateurs / appareils. Le délai d'attente 777 est un peu prudent, mais nous chargeons beaucoup de données sur chaque page, donc à travers des tests, cela était nécessaire. Un 237 plus court pourrait fonctionner pour la plupart des applications.

Todd
la source
1

J'ai rencontré ce problème en créant un site avec Gatsby dont le lien est construit au-dessus de Reach Router. Il semble étrange que ce soit une modification qui doit être faite plutôt que le comportement par défaut.

Quoi qu'il en soit, j'ai essayé plusieurs des solutions ci-dessus et la seule qui a réellement fonctionné pour moi était:

document.getElementById("WhateverIdYouWantToScrollTo").scrollIntoView()

Je l'ai mis dans un useEffect mais vous pouvez tout aussi facilement le mettre dans componentDidMount ou le déclencher de toute autre manière que vous le souhaitez.

Je ne sais pas pourquoi window.scrollTo (0, 0) ne fonctionnerait pas pour moi (et pour les autres).

jj0b
la source
0

Toutes les solutions parlent d'ajouter le scroll sur componentDidMountou componentDidUpdatemais avec le DOM.

J'ai fait tout cela et je n'ai pas travaillé.

Alors, j'ai trouvé une autre façon qui fonctionne très bien pour moi.

Ajouté componentDidUpdate() { window.scrollTo(0, 0) } sur l'en-tête, que le mien est hors de l' <Switch></Switch>élément. Simplement gratuit dans l'application. Travaux.

J'ai aussi trouvé quelque chose de ScrollRestoration , mais je suis paresseux maintenant. Et pour l'instant je vais le garder de la manière "DidUpdate".

J'espère que ça aide!

fais attention

Saison Buzzcut
la source
0

Ce code provoquera un comportement fluide sur le défilement :

<div onClick={() => {
   ReactDOM.findDOMNode(this.headerRef)
      .scrollIntoView({behavior: "smooth"});
                }} 
  className='go-up-button' >
</div>

Vous pouvez passer d'autres paramètres à l'intérieur de scrollIntoView () La syntaxe suivante peut être utilisée:

element.scrollIntoView();
element.scrollIntoView(alignToTop); // Boolean parameter
element.scrollIntoView(scrollIntoViewOptions); // Object parameter

alignToTop Facultatif Est une valeur booléenne:

If true, the top of the element will be aligned to the top of the visible area of the scrollable ancestor. Corresponds to scrollIntoViewOptions: {block: "start", inline: "nearest"}. This is the default value.
If false, the bottom of the element will be aligned to the bottom of the visible area of the scrollable ancestor. Corresponds to scrollIntoViewOptions: {block: "end", inline: "nearest"}.

scrollIntoViewOptions Facultatif Est un objet avec les propriétés suivantes:

*behavior* Optional
    Defines the transition animation.
    One of "auto", "instant", or "smooth". Defaults to "auto".
*block* Optional
    One of "start", "center", "end", or "nearest". Defaults to "center".
*inline* Optional
    One of "start", "center", "end", or "nearest". Defaults to "nearest".

Plus de détails peuvent être trouvés ici: MDN docs

Abhay Shiro
la source
0

Aucune des réponses ci-dessus ne fonctionne actuellement pour moi. Il s'avère que ce .scrollTon'est pas aussi largement compatible que .scrollIntoView.

Dans notre App.js, dans componentWillMount()nous avons ajouté

    this.props.history.listen((location, action) => {
            setTimeout(() => { document.getElementById('root').scrollIntoView({ behavior: "smooth" }) }, 777)
        })

C'est la seule solution qui fonctionne universellement pour nous. rootest l'ID de notre application. Le comportement «fluide» ne fonctionne pas sur tous les navigateurs / appareils. Le délai d'attente 777 est un peu prudent, mais nous chargeons beaucoup de données sur chaque page, donc à travers des tests, cela était nécessaire. Un 237 plus court pourrait fonctionner pour la plupart des applications.

Todd
la source
0

Si je suppose que vous rendez un chapitre, disons, un livre par page, tout ce que vous avez à faire est de l'ajouter à votre code. Cela a fonctionné pour moi comme par magie.

    componentDidUpdate(prevProps) {
      if (prevProps.currentChapter !== this.props.currentChapter) {
        window.scrollTo(0, 0);
      }
    }

Avec cela, vous n'avez pas besoin de créer une référence sur le composant en cours de rendu.

Awa Dieudone
la source
0

C'est ce que j'ai fait:

...
useEffect(() => ref.current.scrollTo(0, 0));
const ref = useRef()

       return(
         <div ref={ref}>
           ...
         </div>
        )
...
réagir développeur
la source
0

Vous pouvez utiliser, cela fonctionne pour moi.

import React, { useEffect } from 'react';

useEffect(() => {
    const body = document.querySelector('#root');

    body.scrollIntoView({
        behavior: 'smooth'
    }, 500)

}, []);
bellabelle
la source
-1

Quelque chose comme ça a fonctionné pour moi sur un composant:

<div ref="scroller" style={{height: 500, overflowX: "hidden", overflowY: "auto"}}>
      //Content Here
</div>

Ensuite, quelle que soit la fonction qui traite des mises à jour:

this.refs.scroller.scrollTop=0
kojow7
la source
-1

Rien n'a fonctionné pour moi mais:

componentDidMount(){

    $( document ).ready(function() {
        window.scrollTo(0,0);
    });
}
gal007
la source