Desplácese hasta la parte superior de la página después de renderizar en react.js


168

Tengo un problema, que no tengo ideas, cómo resolverlo. En mi componente reaccionar, muestro una larga lista de datos y pocos enlaces en la parte inferior. Después de hacer clic en cualquiera de estos enlaces, completo la lista con una nueva colección de enlaces y necesito desplazarme hacia la parte superior.

El problema es: ¿cómo desplazarse a la parte superior después de que se procese la nueva colección?

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

Respuestas:


327

Finalmente ... usé:

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

EDITAR: Reaccionar v16.8 +

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

2
Esta es la única solución que funcionó para mí. También intentado: ReactDOM.findDOMNode (this) .scrollTop = 0 y componentDidMount () {this._div.scrollTop = 0} render () {return <div ref = {(ref) => this._div = ref} />}
Michael Bushe

Según W3Schools, esta solución es actualmente compatible con todos los navegadores. Además, la biblioteca ReactDOM está en desuso en futuras versiones de React.
BishopZ

2
@Tomasz: descubrí que todavía tenía estos problemas a veces cuando tenía ciertos divs establecidos en height o min-height: 100%. Tuve que quitarlo y envolverlo en uno de los padres o moverme más hacia el árbol donde aún podía desplazarse
racémico

2
Esto funcionó para mí, pero no en componentDidMount, ya que CDM no puede activarse cuando el cambio de estado da como resultado una nueva representación de la página. Entonces ponga esta llamada - window.scrollTo (0, 0); - Donde sea que cambies de estado.
Puneet Lamba

44
Para aquellos que usan ganchos, el siguiente código funcionará. React.useEffect(() => { window.scrollTo(0, 0); }, []); Tenga en cuenta que también puede importar useEffect directamente:import { useEffect } from 'react'
Powderham

72

Dado que la solución original se proporcionó para una versión muy temprana de react , aquí hay una actualización:

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, ¿puedes estar en react15? intente importar ReactDOM y hacerlo ReactDOM.findDOMNode(this).scrollTop = 0
Marcus Ericsson

12
this is undefined in arrow functionsEs incorrecto. La palabra clave this está vinculada al mismo contexto que la función adjunta developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Joe Delgado

Si es posible, debe evitar ReactDom.findDOMNode (). Use una referencia en su lugar. He publicado una solución mediante desplazamiento suave aquí
bbrinx

default.a.createRef no es una función
Anupam Maurya

48

Podrías usar algo como esto. ReactDom es para reaccionar.14. Solo reacciona de otra manera.

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

Actualización 5/11/2019 para 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)
    }
  }


De todas las sugerencias en esta página, esta es la única que funciona para mí.
Josh F

Nota: si componentDidUpdate no funciona para usted, componentDidMountes otra alternativa.
Alex Fallenstedt

findDOMNode es una trampilla de escape utilizada para acceder al nodo DOM subyacente. En la mayoría de los casos, se desaconseja el uso de esta trampilla de escape porque perfora la abstracción del componente. Ha quedado en desuso en StrictMode. reactjs.org/docs/react-dom.html
Vivek Kumar el

16

En React Routing existe el problema de que si redirigimos a la nueva ruta, no lo llevará automáticamente a la parte superior de la página.

Incluso yo tuve el mismo problema.

Acabo de agregar una sola línea a mi componente y funcionó como la mantequilla.

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

Consulte: reaccionar entrenamiento


¿Es esta la forma recomendada si uso esto para mi botón de 'saltar al principio'? o si hay una manera de 'reaccionar' donde no usamos el objeto de ventana?
Toxnyc

1
Gracias por traer el aviso, la solución que he dado es aplicable para una versión dom de react-router menor que v5, estaba usando v4.2.2 y allí cuando navegas a otra página no te llevaron de forma predeterminada a la parte superior de la página, por lo que tenemos que llevar manualmente al usuario a la parte superior de la página después de la navegación, pero con v5.0.1 react-router dom dejó de enviar la restauración de desplazamiento de la caja, porque según su documento dicen que los navegadores comenzaron a admitir esta característica de forma predeterminada y con la última versión de react-router-dom lo llevará a la parte superior de la página después de la navegación.
Vishal Shetty el

1
@Toxnyc, por lo que usar JavaScript es lo que es Javascript, si reaccionar está por encima de Javascript, incluso si usa cualquiera de los complementos React detrás de escena, solo usará JavaScript y objetos de ventana, según mi conocimiento, el documento de reacción no tiene cualquier cosa por la cual podamos obtener los detalles de la pantalla de la ventana. tenemos que usar Javascript para que funcione.
Vishal Shetty el

13

Esto podría, y probablemente debería, manejarse usando referencias :

"... puede usar ReactDOM.findDOMNode como una" escotilla de escape "pero no lo recomendamos ya que rompe la encapsulación y en casi todos los casos hay una forma más clara de estructurar su código dentro del modelo React".

Código de ejemplo:

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

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

Esto funciona muy bien. Gracias. Para que quede claro, puse el <div ref={(ref) => this._div = ref} />primero <div>de mi declaración de render. El resto de mi render permanece exactamente igual.
Josh F

En caso de que esté usando componentes con estilo, deberá usar "innerRef" en lugar de "ref". Gran solución
furcicm el

Totalmente funciona Para lo que estaba trabajando, podría ser aún más simple <div ref="main">y luegothis.refs.main.scrollTop=0
chuckfactory

2
Es probable que las referencias de configuración de @chuckfactory con cadenas probablemente se eliminen en algún momento, y en realidad tiene algunos inconvenientes interesantes sobre los que quizás desee aprender. news.ycombinator.com/edit?id=12093234
NJensen

10

Puede hacer esto en el enrutador así:

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

El onUpdate={() => window.scrollTo(0, 0)}poner el pergamino superior. Para más información consultar: enlace codepen


solución elegante que solo requiere un pequeño cambio de código en el enrutador en lugar de que cada componente lo maneje por sí mismo. <3
alengel

Desafortunadamente, onUpdate se dispara con cada nueva rutaParam enrutada en una ruta determinada. Entonces, si por ejemplo tiene una página con un montón de imágenes, y si pudiera expandir la imagen en modo modal al hacer clic en cambiar la ruta /somePage/:imgId, se desplazará hacia arriba :(. Cualquier forma de "controlar" si disparar o no el evento onUpdate en rutas / parámetros específicos?
connected_user

Cuando probé esto, TypeScript se quejó de que onUpdateno existe en los accesorios de HashRouter ... Si alguien encuentra el mismo problema: terminé usando la solución ScrollToTop descrita más abajo (y en los documentos react-router) que funcionó perfectamente para mí.
Nicole

9

Para aquellos que usan ganchos, el siguiente código funcionará.

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

Tenga en cuenta que también puede importar useEffect directamente: import { useEffect } from 'react'


1
El []segundo parámetro significa que solo sucederá en el primer renderizado, ¿lo has intentado sin él?
Powderham

8

Aquí hay otro enfoque que le permite elegir a qué componentes montados desea restablecer la posición de desplazamiento de la ventana sin duplicar en masa ComponentDidUpdate / ComponentDidMount.

El siguiente ejemplo está envolviendo el componente Blog con ScrollIntoView (), de modo que si la ruta cambia cuando se monta el componente Blog, ComponentDidUpdate del HOC actualizará la posición de desplazamiento de la ventana.

Puede envolverlo fácilmente en toda la aplicación, de modo que en cualquier cambio de ruta, se activará un reinicio de la ventana.

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

El ejemplo anterior funciona muy bien, pero si ha migrado a react-router-dom, entonces puede simplificar lo anterior creando un HOCque envuelva el componente.

Una vez más, también podría envolverlo con la misma facilidad en sus rutas (simplemente cambie el componentDidMountmétodo al componentDidUpdatecódigo de ejemplo del método escrito anteriormente, así como envolver ScrollIntoViewcon withRouter).

contenedores / ScrollIntoView.js

import { PureComponent, Fragment } from "react";

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

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

export default ScrollIntoView;

componentes / 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 me está dando el siguiente error "expresión no utilizada, esperaba una asignación o llamada de función"
EX0MAK3R

@ EX0MAK3R - Respuesta actualizada.
Matt Carlotta

7

Esto es lo único que funcionó para mí (con un componente de clase ES6):

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

Igualmente. Probé todas las otras soluciones y esta es la única que funcionó para mí.
Erik James Robles

7

Estoy usando el componente ScrollToTop de react-router cuyo código se describe en los documentos de react-router

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

Estoy cambiando el código en un solo archivo de rutas y después de eso no es necesario cambiar el código en cada componente.

Código de ejemplo -

Paso 1: crea el 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)

Paso 2: en el archivo App.js, agregue el componente ScrollToTop después de <Router

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

¡Muy buena solución! Si tienes rutas, solo preséntalas en la parte superior de tus rutas, pero debajo del enrutador. No tuve que cambiar cada componente.
erupción

5

Solución de gancho :

  • Crear un gancho 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);
  • Envuelva su aplicación con ella

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

Documentación: https://reacttraining.com/react-router/web/guides/scroll-restoration



4

Todo lo anterior no funcionó para mí, no estoy seguro de por qué, pero:

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

trabajado, donde HEADER es la identificación de mi elemento de encabezado


Utilicé un gancho useEffect pero funcionó muy bien para mí en un proyecto de Gatsby. ¡Gracias!
jj0b

4

Si todo lo que quieres hacer es algo simple, aquí hay una solución que funcionará para todos

agrega esta mini función

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

llame a la función como sigue desde el pie de página de la página

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

si quieres agregar estilos agradables aquí está el 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;
}


el fragmento de código no parece funcionar. Pero la solución funcionó para mí. Gracias y saludos!
Globefire

3

Estoy usando React Hooks y quería algo reutilizable, pero también algo que pudiera llamar en cualquier momento (en lugar de solo después de renderizar).

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

Luego, para usar el gancho puedes hacer:

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

Gran solución!
Nick

2

Si está haciendo esto para dispositivos móviles , al menos con Chrome, verá una barra blanca en la parte inferior.

Esto sucede cuando la barra de URL desaparece. Solución:

Cambie el css para height / min-height: 100% a height / min-height: 100vh .

Documentos para desarrolladores de Google


1

Ninguna de las respuestas anteriores está funcionando actualmente para mí. Resulta que .scrollTono es tan ampliamente compatible como.scrollIntoView .

En nuestro App.js, componentWillMount()agregamos

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

Esta es la única solución que funciona universalmente para nosotros. root es la ID de nuestra aplicación. El comportamiento "fluido" no funciona en todos los navegadores / dispositivos. El tiempo de espera del 777 es un poco conservador, pero cargamos una gran cantidad de datos en cada página, por lo que fue necesario realizar pruebas. Un 237 más corto podría funcionar para la mayoría de las aplicaciones.


1

Me encontré con este problema al construir un sitio con Gatsby cuyo enlace se basa en Reach Router. Parece extraño que se trate de una modificación que debe realizarse en lugar del comportamiento predeterminado.

De todos modos, probé muchas de las soluciones anteriores y la única que realmente funcionó para mí fue:

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

Puse esto en useEffect, pero podría ponerlo fácilmente en componentDidMount o activarlo de cualquier otra manera que lo desee.

No estoy seguro de por qué window.scrollTo (0, 0) no funcionaría para mí (y para otros).


0

Todas las soluciones hablan de agregar el desplazamiento en componentDidMounto componentDidUpdatepero con el DOM.

Hice todo eso y no funcionó.

Entonces, descubrí otra forma que funciona bien para mí.

Agregado componentDidUpdate() { window.scrollTo(0, 0) } en el encabezado, ese mío está fuera del <Switch></Switch>elemento. Simplemente gratis en la aplicación. Trabajos.

También encontré algo sobre ScrollRestoration , pero ahora soy vago. Y por ahora lo mantendré en la forma "DidUpdate".

¡Espero eso ayude!

cuidate


0

Este código causará un comportamiento suave en el desplazamiento :

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

Puede pasar otros parámetros dentro de scrollIntoView () Se puede usar la siguiente sintaxis:

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

alignToTop Opcional es un valor 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 Opcional Es un objeto con las siguientes propiedades:

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

Más detalles se pueden encontrar aquí: documentos de MDN


0

Ninguna de las respuestas anteriores está funcionando actualmente para mí. Resulta que .scrollTono es tan ampliamente compatible como.scrollIntoView .

En nuestro App.js, componentWillMount()agregamos

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

Esta es la única solución que funciona universalmente para nosotros. rootes la identificación de nuestra aplicación. El comportamiento "fluido" no funciona en todos los navegadores / dispositivos. El tiempo de espera del 777 es un poco conservador, pero cargamos una gran cantidad de datos en cada página, por lo que fue necesario realizar pruebas. Un 237 más corto podría funcionar para la mayoría de las aplicaciones.


0

Si supongo que está representando un capítulo de say, un libro por página, todo lo que necesita hacer es agregar esto a su código. Esto funcionó para mí como magia.

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

Con esto, no tiene necesidad de crear una referencia en el componente que se está representando.


0

Esto es lo que hice:

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

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

0

Puedes usar, esto funciona para mí.

import React, { useEffect } from 'react';

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

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

}, []);

-1

Algo como esto me funcionó en un componente:

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

Luego, en cualquier función que se trate de actualizaciones:

this.refs.scroller.scrollTop=0

-1

Nada funcionó para mí pero:

componentDidMount(){

    $( document ).ready(function() {
        window.scrollTo(0,0);
    });
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.