Scorri fino all'inizio della pagina dopo il rendering in React.js


169

Ho un problema, che non ho idee, come risolverlo. Nel mio componente di reazione visualizzo un lungo elenco di dati e alcuni collegamenti in fondo. Dopo aver fatto clic su uno di questi collegamenti, compilo l'elenco con una nuova raccolta di collegamenti e devo scorrere verso l'alto.

Il problema è: come scorrere verso l'alto dopo il rendering della nuova raccolta?

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

Risposte:


327

Finalmente .. ho usato:

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

EDIT: React v16.8 +

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

2
Questa è l'unica soluzione che ha funzionato per me. Provato anche: ReactDOM.findDOMNode (this) .scrollTop = 0 e componentDidMount () {this._div.scrollTop = 0} render () {return <div ref = {(ref) => this._div = ref} />}
Michael Bushe,

Secondo W3Schools, questa soluzione è attualmente supportata da tutti i browser. Anche la libreria ReactDOM sarà deprecata nelle versioni future di React.
BishopZ,

2
@Tomasz - Ho scoperto che avevo ancora questi problemi a volte quando avevo alcuni div impostati su altezza o altezza minima: 100%. Ho dovuto rimuoverlo o avvolgerlo in un genitore o spostarmi ulteriormente nell'albero dove poteva ancora scorrere
racemico,

2
Questo ha funzionato per me, ma non con componentDidMount, poiché CDM potrebbe non essere attivato quando il cambio di stato comporta un nuovo rendering della pagina. Quindi metti questa chiamata - window.scrollTo (0, 0); - ovunque si cambi lo stato.
Puneet Lamba,

4
Per coloro che usano hook, funzionerà il seguente codice. React.useEffect(() => { window.scrollTo(0, 0); }, []); Nota, puoi anche importare direttamente useEffect:import { useEffect } from 'react'
Powderham

72

Poiché la soluzione originale è stata fornita per la versione molto precoce di reagire , ecco un aggiornamento:

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

this.getDOMNode === undefined
Dave Lunny,

1
@DaveLunny potresti essere su un reattivo15? prova a importare ReactDOM e a farlo ReactDOM.findDOMNode(this).scrollTop = 0
Marcus Ericsson

12
this is undefined in arrow functionsnon è corretto. Questa parola chiave è associata allo stesso contesto della funzione che racchiude developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Joe Delgado,

Se possibile, dovresti evitare ReactDom.findDOMNode (). Utilizzare invece un riferimento. Ho pubblicato una soluzione usando lo scorrimento regolare qui
bbrinx,

default.a.createRef non è una funzione
Anupam Maurya

48

Potresti usare qualcosa del genere. ReactDom è per reazioni.14. Reagisci altrimenti.

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

Aggiornamento del 5/11/2019 per 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)
    }
  }


Di tutti i suggerimenti in questa pagina, questo è l'unico che funziona per me.
Josh F,

Nota: se componentDidUpdate non funziona per te, componentDidMountè un'altra alternativa.
Alex Fallenstedt,

findDOMNode è un tratteggio di escape utilizzato per accedere al nodo DOM sottostante. Nella maggior parte dei casi, l'uso di questo tratteggio di escape è scoraggiato perché perfora l'astrazione del componente. È stato deprecato in StrictMode. reazionejs.org/docs/react-dom.html
Vivek Kumar

16

In React Routing c'è il problema che se reindirizziamo al nuovo percorso, non ti porterà automaticamente in cima alla pagina.

Anche io ho avuto lo stesso problema.

Ho appena aggiunto la linea singola al mio componente e ha funzionato come il burro.

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

Consultare: reagire alla formazione


è questo il modo consigliato se lo uso per il mio pulsante "Vai all'inizio"? o se esiste un modo di "reazione" in cui non utilizziamo l'oggetto finestra?
Toxnyc,

1
Grazie per avermi avvisato, la soluzione che ho fornito è applicabile per una versione dom del reagente-router inferiore alla v5, stavo usando v4.2.2 e lì quando navighi su un'altra pagina non sei stato portato di default all'inizio della la pagina, quindi dobbiamo portare manualmente l'utente in cima alla pagina dopo la navigazione, ma con dom di reazione-router v5.0.1 ha smesso di spedire il ripristino dello scroll fuori dalla scatola, perché secondo il loro documento dicono che i browser hanno iniziato a supportare questa funzione di default e con l'ultima versione di reagire-router-dom ti porterà in cima alla pagina dopo la navigazione.
Vishal Shetty,

1
@Toxnyc, quindi utilizzare l'oggetto finestra è ciò che è JavaScript, se la reazione è al di sopra di Javascript, anche se si utilizza uno dei plug-in React dietro le quinte, verrà utilizzato solo l'oggetto finestra e Javascript, come da mia conoscenza il documento di reazione non sta avendo qualsiasi cosa con cui possiamo ottenere i dettagli dello schermo della finestra. dobbiamo andare con Javascript per farlo funzionare.
Vishal Shetty,

13

Questo potrebbe e probabilmente dovrebbe essere gestito usando refs :

"... puoi usare ReactDOM.findDOMNode come" tratteggio di escape "ma non lo consigliamo poiché interrompe l'incapsulamento e in quasi tutti i casi esiste un modo più chiaro di strutturare il codice all'interno del modello React."

Codice di esempio:

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

    render() {
        return <div ref={(ref) => this._div = ref} />
    }
}

Funziona benissimo. Grazie. Per essere chiari, ho inserito il <div ref={(ref) => this._div = ref} />primo <div>della mia dichiarazione di rendering. Il resto del mio rendering rimane esattamente lo stesso.
Josh F,

Nel caso in cui si utilizzino componenti Styled, sarà necessario utilizzare "innerRef" anziché "ref". Ottima soluzione
furcicm

Funziona totalmente. Per quello a cui stavo lavorando, potrei essere ancora più semplice con <div ref="main">e poithis.refs.main.scrollTop=0
chuckfactory

2
L'impostazione di @chuckfactory refs utilizzando le stringhe verrà probabilmente rimossa ad un certo punto, e in realtà ha alcuni svantaggi interessanti che potresti voler conoscere. news.ycombinator.com/edit?id=12093234
NJensen

10

Puoi farlo nel router in questo modo:

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

Il onUpdate={() => window.scrollTo(0, 0)}mettere la parte superiore di scorrimento. Per ulteriori informazioni, consultare: collegamento codepen


soluzione elegante che richiede solo una minuscola modifica del codice nel router anziché che ogni componente lo gestisca da solo. <3
alengel,

Purtroppo onUpdate si attiva con ogni nuova route Param indirizzata in una determinata route. Quindi se ad esempio hai una pagina con un mucchio di immagini e se potessi espandere l'immagine in modale quando fai clic cambiando il percorso in /somePage/:imgId, scorrerà verso l'alto :(. Qualsiasi modo per "controllare" se sparare o meno l'evento onUpdate su percorsi / parametri specifici?
connected_user

Quando ho provato questo, TypeScript si è lamentato del fatto che onUpdatenon esiste negli oggetti di scena di HashRouter ... Se qualcuno incontra lo stesso problema: ho finito per usare la soluzione ScrollToTop descritta più avanti (e nei documenti di reazione-router) che ha funzionato perfettamente per me.
Nicole,

9

Per coloro che usano hook, funzionerà il seguente codice.

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

Nota, puoi anche importare direttamente useEffect: import { useEffect } from 'react'


1
Il []secondo parametro indica che accadrà solo sul primo rendering, ci hai provato senza?
Powderham,

8

Ecco ancora un altro approccio che ti consente di scegliere a quali componenti montati vuoi ripristinare la posizione di scorrimento della finestra senza duplicare in massa ComponentDidUpdate / ComponentDidMount.

L'esempio seguente racchiude il componente Blog con ScrollIntoView (), in modo che se il percorso cambia quando viene montato il componente Blog, ComponentDidUpdate dell'HOC aggiornerà la posizione di scorrimento della finestra.

Puoi avvolgerlo facilmente sull'intera app, in modo che, in caso di cambio di rotta, si attivi un ripristino della finestra.

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'esempio sopra funziona alla grande, ma se hai eseguito la migrazione react-router-dom, puoi semplificare quanto sopra creando un HOCelemento che avvolge il componente.

Ancora una volta, puoi anche avvolgerlo facilmente sui tuoi percorsi (basta cambiare il componentDidMountmetodo con il componentDidUpdatecodice di esempio del metodo scritto sopra, così come il wrapping ScrollIntoViewcon withRouter).

contenitori / ScrollIntoView.js

import { PureComponent, Fragment } from "react";

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

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

export default ScrollIntoView;

componenti / Home.js

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

export default () => (
  <ScrollIntoView>
    <div className="container">
      <p>
        Sample Text
      </p>
    </div>
  </ScrollIntoView>
);

ScrollIntoView.js mi dà il seguente errore "espressione inutilizzata, previsto un incarico o chiamata di funzione"
EX0MAK3R

@ EX0MAK3R - Risposta aggiornata.
Matt Carlotta,

7

Questa è l'unica cosa che ha funzionato per me (con un componente di classe ES6):

componentDidMount() {
  ReactDOM.findDOMNode(this).scrollIntoView();
}

Allo stesso modo. Ho provato tutte le altre soluzioni e questa è l'unica che ha funzionato per me.
Erik James Robles,

7

Sto usando il componente ScrollToTop di reagente-router quale codice descritto nei documenti di reagente-router

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

Sto cambiando il codice in un singolo file di route e successivamente non ho bisogno di cambiare il codice in ogni componente.

Codice di esempio -

Passaggio 1: creare il componente 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)

Passaggio 2: nel file App.js, aggiungere il componente ScrollToTop dopo <Router

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

davvero un'ottima soluzione! se hai percorsi basta renderlo nella parte superiore dei tuoi percorsi, ma sotto Router.Non ho dovuto cambiare ogni singolo componente.
eruzione cutanea

5

Soluzione gancio :

  • Creare 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);
  • Avvolgi la tua app con essa

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

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



4

Tutto quanto sopra non ha funzionato per me - non so perché ma:

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

ha funzionato, dove HEADER è l'ID del mio elemento header


Ho usato un hook useEffect, ma per me ha funzionato benissimo in un progetto Gatsby. Grazie!
jj0b,

4

Se tutti vogliono fare è qualcosa di semplice ecco una soluzione che funzionerà per tutti

aggiungi questa mini funzione

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

chiama la funzione come segue dal piè di pagina

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

se vuoi aggiungere dei bei stili ecco il 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;
}


lo snippet di codice non sembra funzionare. Ma la soluzione ha funzionato per me. Grazie e saluti!
globefire

3

Sto usando React Hooks e volevo qualcosa di riutilizzabile ma anche qualcosa che potrei chiamare in qualsiasi momento (piuttosto che subito dopo il rendering).

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

Quindi per usare il gancio puoi fare:

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

Ottima soluzione!
Nick

2

Se lo stai facendo per dispositivi mobili , almeno con Chrome, vedrai una barra bianca in basso.

Questo succede quando la barra degli URL scompare. Soluzione:

Modifica i css per altezza / altezza minima: 100% in altezza / altezza minima: 100 Vh .

Documenti per sviluppatori Google


1

Nessuna delle risposte sopra sta attualmente lavorando per me. Si scopre che .scrollTonon è così compatibile come .scrollIntoView.

Nel nostro App.js, in componentWillMount()abbiamo aggiunto

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

Questa è l'unica soluzione che funziona universalmente per noi. root è l'ID della nostra app. Il comportamento "regolare" non funziona su tutti i browser / dispositivi. Il timeout 777 è un po 'conservativo, ma cariciamo molti dati su ogni pagina, quindi attraverso il test è stato necessario. Un 237 più corto potrebbe funzionare per la maggior parte delle applicazioni.


1

Mi sono imbattuto in questo problema costruendo un sito con Gatsby il cui Link è stato creato su Reach Router. Sembra strano che si debba apportare una modifica anziché il comportamento predefinito.

Comunque, ho provato molte delle soluzioni sopra e l'unica che ha funzionato per me è stata:

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

L'ho inserito in useEffect ma potresti semplicemente inserirlo in componentDidMount o attivarlo in qualsiasi altro modo.

Non sono sicuro del motivo per cui window.scrollTo (0, 0) non funzionerebbe per me (e altri).


0

Tutte le soluzioni parlano dell'aggiunta dello scroll su componentDidMounto componentDidUpdatema con il DOM.

Ho fatto tutto questo e non ho funzionato.

Quindi, ho capito in qualche altro modo che funziona bene per me.

Aggiunto componentDidUpdate() { window.scrollTo(0, 0) } nell'intestazione, il mio è fuori <Switch></Switch>dall'elemento. Solo gratis nell'app. Lavori.

Ho anche scoperto qualcosa su ScrollRestoration , ma ora sono pigro. E per ora lo terrò nel modo "DidUpdate".

Spero che sia d'aiuto!

stai attento


0

Questo codice causerà un comportamento regolare sullo scorrimento :

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

È possibile passare altri parametri all'interno di scrollIntoView () È possibile utilizzare la sintassi seguente:

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

alignToTop Opzionale È un valore booleano:

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 Facoltativo È un oggetto con le seguenti proprietà:

*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".

Maggiori dettagli sono disponibili qui: documenti MDN


0

Nessuna delle risposte sopra sta attualmente lavorando per me. Si scopre che .scrollTonon è così compatibile come .scrollIntoView.

Nel nostro App.js, in componentWillMount()abbiamo aggiunto

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

Questa è l'unica soluzione che funziona universalmente per noi. rootè l'ID della nostra app. Il comportamento "regolare" non funziona su tutti i browser / dispositivi. Il timeout 777 è un po 'prudente, ma cariciamo molti dati su ogni pagina, quindi attraverso il test è stato necessario. Un 237 più corto potrebbe funzionare per la maggior parte delle applicazioni.


0

Se suppongo che stai visualizzando un capitolo di un libro, un libro per pagina, tutto ciò che devi fare è aggiungere questo al tuo codice. Questo ha funzionato per me come per magia.

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

Con questo, non è necessario creare un riferimento al componente da renderizzare.


0

Questo è quello che ho fatto:

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

       return(
         <div ref={ref}>
           ...
         </div>
        )
...

0

Puoi usare, questo funziona per me.

import React, { useEffect } from 'react';

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

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

}, []);

-1

Qualcosa del genere ha funzionato per me su un componente:

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

Quindi in qualunque funzione si occupi degli aggiornamenti:

this.refs.scroller.scrollTop=0

-1

Niente ha funzionato per me ma:

componentDidMount(){

    $( document ).ready(function() {
        window.scrollTo(0,0);
    });
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.