Role até o topo da página após renderizar em react.js


168

Eu tenho um problema, que não tenho idéias, como resolver. No meu componente de reação, exibo uma longa lista de dados e alguns links na parte inferior. Depois de clicar em qualquer um desses links, preencheu a lista com uma nova coleção de links e preciso rolar para o topo.

O problema é - como rolar para o topo após a renderização da nova coleção?

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

Respostas:


327

Finalmente .. eu usei:

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

EDIT: Reagir v16.8 +

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

2
Esta é a única solução que funcionou para mim. Também tentei: ReactDOM.findDOMNode (this) .scrollTop = 0 e componentDidMount () {this._div.scrollTop = 0} render () {return <div ref = {(ref) => this._div = ref} />}
22616 Michael Bushe

De acordo com o W3Schools, esta solução é atualmente suportada por todos os navegadores. Além disso, a biblioteca ReactDOM está sendo preterida em versões futuras do React.
precisa saber é o seguinte

2
Tomasz - Descobri que ainda tinha esse problema algumas vezes quando tinha certas divs definidas como height ou min-height: 100%. Eu tinha que remover e quer envolvê-la em um dos pais ou avançar para a árvore onde ele poderia ainda rolagem
racêmica

2
Isso funcionou para mim, mas não em componentDidMount, pois o CDM pode não ser acionado quando a alteração de estado resulta em uma nova renderização da página. Então faça essa chamada - window.scrollTo (0, 0); - onde quer que você mude de estado.
Puneet Lamba

4
Para aqueles que usam ganchos, o código a seguir funcionará. React.useEffect(() => { window.scrollTo(0, 0); }, []); Observe que você também pode importar o useEffect diretamente:import { useEffect } from 'react'
Powderham

72

Como a solução original foi fornecida para a versão muito inicial do react , aqui está uma atualização:

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 === indefinido
Dave Lunny

1
@DaveLunny você pode estar em react15? tente importar ReactDOM e fazendo ReactDOM.findDOMNode(this).scrollTop = 0
Marcus Ericsson

12
this is undefined in arrow functionsestá incorreto. A esta palavra-chave é ligada ao mesmo contexto que a função vedante developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Joe Delgado

Se possível, você deve evitar ReactDom.findDOMNode (). Use uma ref. Eu postei uma solução usando rolagem suave aqui
bbrinx

default.a.createRef não é uma função
Anupam Maurya

48

Você poderia usar algo assim. ReactDom é para react.14. Apenas reaja de outra maneira.

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

Atualização 11/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 as sugestões nesta página, esta é a única que funciona para mim.
21717 Josh F

Nota: se componentDidUpdate não funcionar para você, componentDidMounté outra alternativa.
Alex Fallenstedt

findDOMNode é uma hachura de escape usada para acessar o nó DOM subjacente. Na maioria dos casos, o uso dessa escotilha de escape é desencorajado porque perfura a abstração do componente. Foi preterido no StrictMode. reactjs.org/docs/react-dom.html
Vivek Kumar

16

Em React Routing, há o problema de que, se redirecionarmos para a nova rota, ela não o levará automaticamente ao topo da página.

Até eu tive o mesmo problema.

Acabei de adicionar uma única linha ao meu componente e funcionou como manteiga.

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

Consulte: reagir treinamento


esta é a maneira recomendada se eu usar isso no meu botão 'pular para cima'? ou se existe uma maneira de 'reagir' em que não usamos o objeto window?
Toxnyc 6/08/19

1
Obrigado por trazer para o aviso, a solução que forneci é aplicável a uma versão do dom do roteador de reação inferior à v5, eu estava usando a v4.2.2 e lá quando você navega para outra página, você não é levado por padrão para o topo da página a página, por isso precisamos levar o usuário manualmente para o topo da página após a navegação, mas com a v5.0.1 o dom do roteador parou de enviar a restauração de rolagem para fora da caixa, porque, de acordo com o documento, eles dizem que os navegadores começaram a oferecer suporte esse recurso por padrão e com a versão mais recente do react-router-dom, você será levado ao topo da página após a navegação.
Vishal Shetty

1
@Toxnyc, portanto, usar o objeto de janela é o que é o Javascript, se o React estiver no topo do Javascript, mesmo que você use algum dos plugins do React nos bastidores, ele usará apenas o Javascript e o objeto da janela, de acordo com o meu conhecimento que o documento do react não está tendo qualquer coisa pela qual possamos obter os detalhes da tela da janela. temos que usar o Javascript para fazê-lo funcionar.
Vishal Shetty

13

Isso poderia e provavelmente deveria ser tratado usando refs :

"... você pode usar o ReactDOM.findDOMNode como uma" saída de escape ", mas não o recomendamos, pois ele quebra o encapsulamento e, em quase todos os casos, há uma maneira mais clara de estruturar seu código no modelo React."

Código de exemplo:

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

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

Isso funciona muito bem. Obrigado. Para ser claro, eu coloquei o <div ref={(ref) => this._div = ref} />primeiro na <div>minha declaração de renderização. O restante da minha renderização permanece exatamente o mesmo.
21417 Josh F

Caso você esteja usando componentes com estilo, precisará usar "innerRef" em vez de "ref". Excelente solução
furcicm

Totalmente funciona. Para o que eu estava trabalhando, eu poderia ser ainda mais simples com <div ref="main">e depoisthis.refs.main.scrollTop=0
chuckfactory

2
A configuração @chuckfactory refs usando strings provavelmente será removida em algum momento e, na verdade, tem algumas desvantagens interessantes sobre as quais você pode querer aprender. news.ycombinator.com/edit?id=12093234 #
NJensen

10

Você pode fazer isso no roteador assim:

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

O onUpdate={() => window.scrollTo(0, 0)}colocar o topo da rolagem. Para mais informações, consulte: link codepen


solução elegante que requer apenas uma pequena alteração de código no roteador, em vez de cada componente cuidar dele. <3
alengel

Infelizmente, o onUpdate é acionado a cada novo routeParam roteado em uma determinada rota. Portanto, se você tem uma página com um monte de imagens, por exemplo, e pode expandir a imagem em um modal ao clicar em alterar a rota para /somePage/:imgId, ela rolará para cima :(. Qualquer forma de "controlar" se deve ou não ser acionada o evento onUpdate em rotas / parâmetros específicos?
connected_user

Quando tentei isso, o TypeScript reclamou que onUpdatenão existe nos objetos do HashRouter ... Se alguém encontrar o mesmo problema: acabei usando a solução ScrollToTop descrita mais abaixo (e nos documentos do roteador de reação) que funcionou perfeitamente para mim.
277 Nicole

9

Para aqueles que usam ganchos, o código a seguir funcionará.

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

Observe que você também pode importar o useEffect diretamente: import { useEffect } from 'react'


1
O []segundo parâmetro significa que isso só acontecerá na primeira renderização, você já tentou sem?
Powderham

8

Aqui está outra abordagem que permite escolher em quais componentes montados você deseja redefinir a posição de rolagem da janela sem duplicar em massa o ComponentDidUpdate / ComponentDidMount.

O exemplo abaixo está agrupando o componente Blog com ScrollIntoView (), para que, se a rota for alterada quando o componente Blog estiver montado, o ComponentDidUpdate do HOC atualizará a posição de rolagem da janela.

Você pode facilmente envolvê-lo em todo o aplicativo, para que, em qualquer mudança de rota, ele acione uma redefinição da janela.

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

O exemplo acima funciona muito bem, mas se você migrou para react-router-dom, poderá simplificar o acima, criando um HOCque envolva o componente.

Mais uma vez, você também pode facilmente envolvê-lo em suas rotas (basta alterar o componentDidMountmétodo para o componentDidUpdatecódigo de exemplo do método escrito acima, bem como empacotar ScrollIntoViewcomwithRouter ).

containers / ScrollIntoView.js

import { PureComponent, Fragment } from "react";

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

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

export default ScrollIntoView;

components / Home.js

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

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

O ScrollIntoView.js está me fornecendo o seguinte erro "expressão não utilizada, espera-se uma atribuição ou chamada de função"
EX0MAK3R 19/10/1918

@ EX0MAK3R - Resposta atualizada.
Matt Carlotta

7

Esta é a única coisa que funcionou para mim (com um componente de classe ES6):

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

Da mesma forma. Eu tentei todas as outras soluções e esta é a única que funcionou para mim.
Erik James Robles

7

Estou usando o componente ScrollToTop do react-router, código descrito nos documentos do react-router

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

Estou mudando o código em um único arquivo de rotas e depois disso não há necessidade de alterar o código em todos os componentes.

Código de exemplo -

Etapa 1 - crie o 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)

Etapa 2 - No arquivo App.js, adicione o Componente ScrollToTop após <Router

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

solução realmente boa! ​​se você tiver rotas, apenas a torne no topo de suas rotas, mas abaixo do roteador. Eu não precisava alterar todos os componentes.
rash

5

Solução de gancho :

  • Criar um 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);
  • Envolva seu aplicativo com ele

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

Documentação: https://reacttraining.com/react-router/web/guides/scroll-restoration



4

Todas as opções acima não funcionaram para mim - não sei por que, mas:

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

funcionou, em que HEADER é o ID do meu elemento de cabeçalho


Eu usei um gancho useEffect, mas isso funcionou muito bem para mim em um projeto Gatsby. Obrigado!
Jj0b

4

Se tudo o que queremos é algo simples, aqui está uma solução que funcionará para todos

adicione esta mini função

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

chame a função da seguinte forma no rodapé da página

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

se você quiser adicionar estilos legais, aqui está o 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;
}


o trecho de código não parece funcionar. Mas a solução funcionou para mim. Obrigado e felicidades!
globefire 12/04

3

Estou usando o React Hooks e queria algo reutilizável, mas também algo que eu pudesse chamar a qualquer momento (e não apenas após a renderização).

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

Então, para usar o gancho, você pode:

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

Ótima solução!
Nick

2

Se você estiver fazendo isso para celular , pelo menos com o chrome, verá uma barra branca na parte inferior.

Isso acontece quando a barra de URL desaparece. Solução:

Altere o css para height / min-height: 100% para height / min-height: 100vh .

Documentos do desenvolvedor do Google


1

Nenhuma das respostas acima está funcionando para mim. Acontece que .scrollTonão é tão amplamente compatível quanto.scrollIntoView .

Em nosso App.js, componentWillMount()adicionamos

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

Esta é a única solução que está funcionando universalmente para nós. root é o ID do nosso aplicativo. O comportamento "suave" não funciona em todos os navegadores / dispositivos. O tempo limite do 777 é um pouco conservador, mas carregamos muitos dados em todas as páginas; portanto, ao testar isso era necessário. Um 237 mais curto pode funcionar para a maioria dos aplicativos.


1

Eu me deparei com esse problema criando um site com o Gatsby, cujo Link é construído sobre o Reach Router. Parece estranho que seja uma modificação que precisa ser feita, e não o comportamento padrão.

Enfim, tentei muitas das soluções acima e a única que realmente funcionou para mim foi:

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

Coloquei isso em um useEffect, mas você poderia colocá-lo com a mesma facilidade em componentDidMount ou acioná-lo da maneira que desejasse.

Não sei por que o window.scrollTo (0, 0) não funcionaria para mim (e para outros).


0

Todas as soluções falam sobre adicionar o pergaminho componentDidMountoucomponentDidUpdate com o DOM.

Eu fiz tudo isso e não funcionou.

Então, descobri alguma outra maneira que funciona muito bem para mim.

Adicionado componentDidUpdate() { window.scrollTo(0, 0) } no cabeçalho, esse meu está fora do <Switch></Switch>elemento. Apenas grátis no aplicativo. Trabalho.

Eu também encontrei sobre alguns ScrollRestoration , mas agora sou preguiçoso. E por enquanto vamos mantê-lo da maneira "DidUpdate".

Espero que ajude!

esteja a salvo


0

Este código causará um comportamento suave no pergaminho :

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

Você pode passar outros parâmetros dentro da scrollIntoView (). A sintaxe a seguir pode ser usada:

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

alignToTop Opcional É um 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 É um objeto com as seguintes propriedades:

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

Mais detalhes podem ser encontrados aqui: MDN docs


0

Nenhuma das respostas acima está funcionando para mim. Acontece que .scrollTonão é tão amplamente compatível quanto.scrollIntoView .

Em nosso App.js, componentWillMount()adicionamos

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

Esta é a única solução que está funcionando universalmente para nós. rooté o ID do nosso aplicativo. O comportamento "suave" não funciona em todos os navegadores / dispositivos. O tempo limite do 777 é um pouco conservador, mas carregamos muitos dados em todas as páginas; portanto, ao testar isso era necessário. Um 237 mais curto pode funcionar para a maioria dos aplicativos.


0

Se eu assumir que você está processando um capítulo, digamos, um livro por página, tudo o que você precisa fazer é adicionar isso ao seu código. Isso funcionou para mim como mágica.

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

Com isso, você não precisa criar uma referência no componente que está sendo renderizado.


0

Isto é o que eu fiz:

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

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

0

Você pode usar, isso funciona para mim.

import React, { useEffect } from 'react';

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

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

}, []);

-1

Algo assim funcionou para mim em um componente:

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

Então, em qualquer função que esteja lidando com atualizações:

this.refs.scroller.scrollTop=0

-1

Nada funcionou para mim, mas:

componentDidMount(){

    $( document ).ready(function() {
        window.scrollTo(0,0);
    });
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.