Przewiń na górę strony po wyrenderowaniu w respond.js


168

Mam problem, na który nie mam pomysłu, jak go rozwiązać. W moim komponencie reagowania wyświetlam długą listę danych i kilka linków na dole. Po kliknięciu któregokolwiek z tych linków wypełniam listę nowym zbiorem linków i muszę przewinąć do góry.

Problem w tym, jak przewinąć do góry po wyrenderowaniu nowej kolekcji?

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

Odpowiedzi:


327

Wreszcie… użyłem:

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

EDYCJA: React v16.8 +

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

2
To jedyne rozwiązanie, które u mnie zadziałało. Próbowano również: ReactDOM.findDOMNode (this) .scrollTop = 0 i componentDidMount () {this._div.scrollTop = 0} render () {return <div ref = {(ref) => this._div = ref} />}
Michael Bushe

Według W3Schools to rozwiązanie jest obecnie obsługiwane przez wszystkie przeglądarki. Również biblioteka ReactDOM jest przestarzała w przyszłych wersjach React.
BishopZ

2
@Tomasz - Okazało się, że czasami nadal mam ten problem, gdy miałem ustawione elementy div na wysokość lub min-wysokość: 100%. Musiałem go usunąć i albo owinąć w rodzica, albo przenieść dalej w drzewo, gdzie nadal można było przewijać
racemiczny

2
To zadziałało dla mnie, ale nie w module componentDidMount, ponieważ CDM może nie zostać uruchomiony, gdy zmiana stanu powoduje ponowne renderowanie strony. Więc uruchom to wywołanie - window.scrollTo (0, 0); - gdziekolwiek to jest, zmieniasz stan.
Puneet Lamba

4
Poniższy kod będzie działał dla osób używających hooków. React.useEffect(() => { window.scrollTo(0, 0); }, []); Uwaga, możesz również importować useEffect bezpośrednio:import { useEffect } from 'react'
Powderham

72

Ponieważ oryginalne rozwiązanie zostało dostarczone dla bardzo wczesnej wersji React , oto aktualizacja:

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, możesz być na act15? spróbuj zaimportować ReactDOM i zrób to ReactDOM.findDOMNode(this).scrollTop = 0
Marcus Ericsson

12
this is undefined in arrow functionsjest nieprawidłowe. Słowo kluczowe this jest powiązane z tym samym kontekstem, co funkcja zamykająca developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Joe Delgado

Jeśli to możliwe, powinieneś unikać ReactDom.findDOMNode (). Zamiast tego użyj ref. Opublikowałem tutaj rozwiązanie wykorzystujące płynne przewijanie
bbrinx

default.a.createRef nie jest funkcją
Anupam Maurya

48

Możesz użyć czegoś takiego. ReactDom służy do reagowania 14. Po prostu zareaguj inaczej.

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

Aktualizacja 5/11/2019 dla 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)
    }
  }


Ze wszystkich sugestii na tej stronie jest to jedyna, która działa dla mnie.
Josh F

Uwaga: jeśli componentDidUpdate nie działa, componentDidMountjest inna alternatywa.
Alex Fallenstedt

findDOMNode to klapa ewakuacyjna używana do uzyskiwania dostępu do bazowego węzła DOM. W większości przypadków odradza się korzystanie z tego włazu ratunkowego, ponieważ przebija on abstrakcję elementu. Został wycofany w StrictMode. actjs.org/docs/react-dom.html
Vivek Kumar

16

W React Routing jest problem polegający na tym, że jeśli przekierujemy na nową trasę, to nie przeniesie Cię to automatycznie na górę strony.

Nawet ja miałem ten sam problem.

Właśnie dodałem pojedynczą linię do mojego komponentu i działała jak masło.

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

Zobacz: trening reagowania


Czy jest to zalecany sposób, jeśli używam tego jako przycisku „Przejdź na górę”? lub jeśli istnieje sposób „reagowania”, w którym nie używamy obiektu okna?
Toxnyc

1
Dziękuję za powiadomienie, rozwiązanie, które podałem, ma zastosowanie do wersji domeny react-routera mniejszej niż v5, używałem wersji 4.2.2 i tam, gdy przechodzisz do innej strony, nie zostałeś domyślnie przeniesiony na początek stronę, więc musimy ręcznie przenieść użytkownika na górę strony po nawigacji, ale w wersji 5.0.1react-router dom przestał wysyłać przywrócenie przewijania po wyjęciu z pudełka, ponieważ zgodnie z ich dokumentem mówią, że przeglądarki zaczęły wspierać ta funkcja jest domyślnie iw najnowszej wersji react-router-dom po nawigacji zostaniesz przeniesiony na górę strony.
Vishal Shetty

1
@Toxnyc, więc używanie obiektu window jest tym, czym jest Javascript.Jeśli reakcja jest nad Javascriptem, to nawet jeśli użyjesz którejkolwiek z wtyczek React za kulisami, będzie ona używać tylko JavaScript i obiektu okna, zgodnie z moją wiedzą, reakcja dokumentu nie ma wszystko, dzięki czemu możemy uzyskać szczegóły ekranu okna. musimy iść z Javascriptem, aby to działało.
Vishal Shetty

13

Można to i prawdopodobnie powinno być obsługiwane za pomocą referencji :

„... możesz użyć ReactDOM.findDOMNode jako„ włazu ucieczki ”, ale nie zalecamy tego, ponieważ przerywa on hermetyzację i prawie w każdym przypadku istnieje jaśniejszy sposób na uporządkowanie kodu w modelu React.”

Przykładowy kod:

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

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

To działa świetnie. Dzięki. Żeby było jasne, umieściłem the <div ref={(ref) => this._div = ref} />w pierwszej <div>części mojego renderowania. Reszta mojego renderowania pozostaje dokładnie taka sama.
Josh F

W przypadku, gdy używasz komponentów Styled, będziesz musiał użyć „innerRef” zamiast „ref”. Świetne rozwiązanie
furcicm

Całkowicie działa. Jeśli chodzi o to, nad czym pracowałem, mógłbym być jeszcze prostszy z, <div ref="main">a potemthis.refs.main.scrollTop=0
chuckfactory

2
@chuckfactory ustawienie refs przy użyciu łańcuchów prawdopodobnie zostanie w pewnym momencie usunięte i ma kilka interesujących wad, o których warto się dowiedzieć. news.ycombinator.com/edit?id=12093234
NJensen

10

Możesz to zrobić na routerze w ten sposób:

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

onUpdate={() => window.scrollTo(0, 0)}Umieścić przewijania szczyt. Więcej informacji znajdziesz pod adresem : codepen link


eleganckie rozwiązanie, które wymaga tylko niewielkiej zmiany kodu w routerze, zamiast obsługiwać go samodzielnie. <3
alengel

Niestety onUpdate odpala z każdym nowym routeParam kierowanym na danej trasie. Więc jeśli na przykład masz stronę z wieloma obrazami i możesz rozszerzyć obraz w trybie modalnym po kliknięciu zmiany trasy na /somePage/:imgId, przewinie się on w górę :(. Każdy sposób "kontrolowania" uruchamiania, czy nie zdarzenie onUpdate na określonych trasach / parametrach?
connected_user

Kiedy próbowałem tego, TypeScript narzekał, że onUpdatenie istnieje w rekwizytach HashRoutera ... Jeśli ktoś napotka ten sam problem: Skończyło się na rozwiązaniu ScrollToTop opisanym poniżej (iw dokumentacji react-router), które działało idealnie dla mnie.
Nicole,

9

Poniższy kod będzie działał dla osób używających hooków.

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

Uwaga, możesz również bezpośrednio importować useEffect: import { useEffect } from 'react'


1
Ponieważ []drugi parametr oznacza, że ​​stanie się to tylko przy pierwszym renderowaniu, czy próbowałeś bez?
Powderham

8

Oto jeszcze jedno podejście, które pozwala wybrać zamontowane komponenty, do których pozycja przewijania okna ma zostać zresetowana bez masowego duplikowania ComponentDidUpdate / ComponentDidMount.

Poniższy przykład przedstawia opakowanie składnika Blog metodą ScrollIntoView (), tak że jeśli trasa zmieni się po zamontowaniu składnika Blog, to ComponentDidUpdate HOC zaktualizuje pozycję przewijania okna.

Możesz równie łatwo zawinąć go w całą aplikację, aby przy każdej zmianie trasy wyzwolił reset okna.

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

Powyższy przykład działa świetnie, ale jeśli przeprowadziłeś migrację do react-router-dom, możesz uprościć powyższe, tworząc element HOCopakowujący komponent.

Po raz kolejny, można też tak łatwo owinąć ją na swoje trasy (tylko zmiana componentDidMountsposobu na componentDidUpdateprzykład kod metody pisemnej powyżej, jak również zawijania ScrollIntoViewz withRouter).

kontenery / ScrollIntoView.js

import { PureComponent, Fragment } from "react";

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

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

export default ScrollIntoView;

komponenty / 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 wyświetla następujący błąd „nieużywane wyrażenie, oczekiwano przypisania lub wywołania funkcji”
EX0MAK3R,

@ EX0MAK3R - Zaktualizowana odpowiedź.
Matt Carlotta,

7

To jedyna rzecz, która u mnie zadziałała (z komponentem klasy ES6):

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

Również. Wypróbowałem wszystkie inne rozwiązania i tylko to działało dla mnie.
Erik James Robles

7

Używam komponentu React-router ScrollToTop, którego kod opisano w dokumentach reagujących na router

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

Zmieniam kod w jednym pliku Routes i po tym nie ma potrzeby zmiany kodu w każdym komponencie.

Przykładowy kod -

Krok 1 - Utwórz składnik 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)

Krok 2 - W pliku App.js dodaj komponent ScrollToTop po <Router

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

naprawdę dobre rozwiązanie! jeśli masz trasy, po prostu wyrenderuj je na górze swoich tras, ale poniżej routera. nie musiałem zmieniać każdego komponentu.
wysypka

5

Rozwiązanie haczykowe :

  • Utwórz zaczep 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);
  • Otocz nią swoją aplikację

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

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



4

Wszystkie powyższe nie zadziałały dla mnie - nie wiem dlaczego, ale:

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

zadziałało, gdzie HEADER to identyfikator mojego elementu nagłówka


Użyłem haka useEffect, ale zadziałało to świetnie w projekcie Gatsby. Dzięki!
jj0b

4

Jeśli wszyscy chcą zrobić coś prostego, oto rozwiązanie, które będzie działać dla każdego

Dodaj tę mini funkcję

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

wywołaj funkcję w następujący sposób ze stopki strony

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

jeśli chcesz dodać ładne style, tutaj jest 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;
}


fragment kodu wydaje się nie działać. Ale rozwiązanie zadziałało dla mnie. Dzięki i pozdrawiam!
globefire

3

Używam haków React i chciałem czegoś do ponownego użycia, ale także czegoś, co mógłbym wywołać w dowolnym momencie (zamiast zaraz po renderowaniu).

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

Następnie, aby użyć haka, możesz:

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

Świetne rozwiązanie!
Nick

2

Jeśli robisz to na telefonie komórkowym , przynajmniej z Chrome, na dole zobaczysz biały pasek.

Dzieje się tak, gdy pasek adresu URL znika. Rozwiązanie:

Zmień css na wysokość / min-wysokość: 100% na wysokość / min-wysokość: 100vh .

Dokumenty Google Developer


1

Żadna z powyższych odpowiedzi nie działa obecnie dla mnie. Okazuje się, że .scrollTonie jest to tak szeroko kompatybilne jak .scrollIntoView.

W naszym App.js componentWillMount()dodaliśmy

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

To jedyne rozwiązanie, które działa dla nas uniwersalnie. root to identyfikator naszej aplikacji. „Płynne” zachowanie nie działa na każdej przeglądarce / urządzeniu. Limit czasu 777 jest nieco konserwatywny, ale ładujemy wiele danych na każdej stronie, więc podczas testów było to konieczne. Krótszy 237 może działać w przypadku większości aplikacji.


1

Natknąłem się na ten problem podczas tworzenia witryny z Gatsby, którego łącze jest zbudowane na routerze Reach. Wydaje się dziwne, że jest to modyfikacja, którą należy wprowadzić, a nie domyślne zachowanie.

W każdym razie wypróbowałem wiele powyższych rozwiązań i jedynym, które faktycznie działało, było:

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

Umieściłem to w useEffect, ale równie łatwo można było umieścić go w komponencie componentDidMount lub uruchomić w inny sposób.

Nie wiem, dlaczego window.scrollTo (0, 0) nie działa dla mnie (i innych).


0

Wszystkie rozwiązania mówią o dodaniu przewijania na componentDidMountlub componentDidUpdateale z DOM.

Zrobiłem to wszystko i nie zadziałałem.

Więc znalazłem inny sposób, który działa dobrze dla mnie.

Dodano componentDidUpdate() { window.scrollTo(0, 0) } na nagłówku, że moja jest poza <Switch></Switch>elementem. Po prostu za darmo w aplikacji. Pracuje.

Znalazłem też coś o ScrollRestoration , ale teraz jestem leniwy. I na razie zachowam to w stylu „DidUpdate”.

Mam nadzieję, że to pomoże!

bądź bezpieczny


0

Ten kod spowoduje płynne zachowanie na przewijaniu :

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

Możesz przekazać inne parametry wewnątrz scrollIntoView (). Można użyć następującej składni:

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

alignToTop Opcjonalne jest wartością logiczną:

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 Opcjonalne jest obiektem z następującymi właściwościami:

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

Więcej szczegółów można znaleźć tutaj: Dokumentacja MDN


0

Żadna z powyższych odpowiedzi nie działa obecnie dla mnie. Okazuje się, że .scrollTonie jest to tak szeroko kompatybilne jak.scrollIntoView.

W naszym App.js componentWillMount()dodaliśmy

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

To jedyne rozwiązanie, które działa dla nas uniwersalnie. rootto identyfikator naszej aplikacji. „Płynne” zachowanie nie działa na każdej przeglądarce / urządzeniu. Limit czasu 777 jest nieco konserwatywny, ale ładujemy wiele danych na każdej stronie, więc podczas testów było to konieczne. Krótszy 237 może działać w przypadku większości aplikacji.


0

Jeśli przyjmuję, że renderujesz, powiedzmy, rozdział, książkę na stronę, wszystko, co musisz zrobić, to dodać to do swojego kodu. To zadziałało na mnie jak magia.

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

Dzięki temu nie ma potrzeby tworzenia odwołania do renderowanego komponentu.


0

Oto co zrobiłem:

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

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

0

Możesz użyć, to działa dla mnie.

import React, { useEffect } from 'react';

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

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

}, []);

-1

Coś takiego zadziałało dla mnie na komponencie:

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

Następnie w dowolnej funkcji zajmującej się aktualizacjami:

this.refs.scroller.scrollTop=0

-1

Nic nie działało, ale:

componentDidMount(){

    $( document ).ready(function() {
        window.scrollTo(0,0);
    });
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.