Прокрутите до верхней части страницы после рендера в Reaction.js.

169

У меня есть проблема, которую я понятия не имею, как решить. В моем компоненте реакции я отображаю длинный список данных и несколько ссылок внизу. После нажатия на любую из этих ссылок я заполняю список новой коллекцией ссылок и должен прокрутиться до самого верха.

Проблема в том, как прокрутить верхнюю панель после отображения новой коллекции.

'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;
Андрей Коваленко
источник

Ответы:

327

Наконец-то .. Я использовал:

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

РЕДАКТИРОВАТЬ: React v16.8 +

useEffect(() => {
  window.scrollTo(0, 0)
}, [])
рацемической
источник
2
Это единственное решение, которое сработало для меня. Также пробовал: ReactDOM.findDOMNode (this) .scrollTop = 0 и componentDidMount () {this._div.scrollTop = 0} render () {return <div ref = {(ref) => this._div = ref} />}
Майкл Буш
Согласно W3Schools, это решение в настоящее время поддерживается всеми браузерами. Также библиотека ReactDOM устарела в будущих версиях React.
BishopZ
2
@Tomasz - я обнаружил, что иногда у меня все еще возникали эти проблемы, когда у некоторых div'ов были установлены высота или минимальная высота: 100%. Я должен был удалить и либо обернуть его в родительском или двигаться дальше в дерево , где он мог бы еще свиток
рацемической
2
Это сработало для меня, но не в componentDidMount, поскольку CDM может не запускаться, когда изменение состояния приводит к повторному рендерингу страницы. Так что поставьте этот вызов - window.scrollTo (0, 0); - где бы вы ни были, вы меняете состояние.
Пунит Ламба
4
Для тех, кто использует хуки, будет работать следующий код. React.useEffect(() => { window.scrollTo(0, 0); }, []); Обратите внимание, вы также можете импортировать useEffect напрямую:import { useEffect } from 'react'
Powderham
72

Так как оригинальное решение было предоставлено для очень ранней версии реакции , вот обновление:

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
Дейв Ланни
1
@DaveLunny, вы можете быть на15? попробуйте импортировать ReactDOM и сделать ReactDOM.findDOMNode(this).scrollTop = 0
Marcus Ericsson
12
this is undefined in arrow functionsэто неверно. Это ключевое слово связано с тем же контекстом, что и включающая функция developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Джо Дельгадо,
Если возможно, вам следует избегать ReactDom.findDOMNode (). Вместо этого используйте ссылку. Я разместил решение с использованием плавной прокрутки здесь
bbrinx
default.a.createRef не является функцией
Anupam Maurya
48

Вы могли бы использовать что-то вроде этого. ReactDom предназначен для реагирования. Просто реагируй иначе.

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

Обновление 5/11/2019 для 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)
    }
  }

Дж. Марк Стивенс
источник
Из всех предложений на этой странице это единственный, который работает для меня.
Джош Ф
Примечание: если componentDidUpdate не работает для вас, componentDidMountэто еще одна альтернатива.
Алекс Фалленштедт
findDOMNode - это аварийный штрих, используемый для доступа к базовому узлу DOM. В большинстве случаев использование этого аварийного люка не рекомендуется, поскольку оно пробивает абстракцию компонента. Это было объявлено устаревшим в StrictMode. actjs.org/docs/react-dom.html
Вивек Кумар,
16

В React Routing существует проблема, заключающаяся в том, что если мы перенаправим на новый маршрут, то он автоматически не приведет вас к началу страницы.

Даже у меня была такая же проблема.

Я просто добавил одну строку в свой компонент, и он работал как масло.

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

Обратитесь: отреагируйте обучение

Вишал Шетти
источник
это рекомендуемый способ, если я использую это для своей кнопки «перейти наверх»? или если есть способ реакции, где мы не используем объект окна?
Toxnyc
1
Спасибо за то, что вы обратили на меня внимание. Решение, которое я дал, применимо для dom-версии реактивного маршрутизатора ниже v5, я использовал v4.2.2, и там, когда вы переходили на другую страницу, вы по умолчанию не переходили наверх страницы, поэтому мы должны вручную переместить пользователя наверх страницы после навигации, но с помощью версии 5.0.1 реакционный маршрутизатор прекратил поставку восстановления прокрутки из коробки, потому что согласно их документу они говорят, что браузеры начали поддерживать эта функция по умолчанию, а с последней версией реакции-роутера-dom вы перейдете наверх страницы после навигации.
Вишал Шетти
1
@Toxnyc, поэтому использование объекта окна - это то, чем является Javascript. Если реакция находится поверх Javascript, то даже если вы используете какой-либо плагин React за кулисами, он будет использовать только Javascript и объект окна, согласно моему мнению, документ реакции не имеет все, что мы можем получить детали экрана окна. мы должны пойти с Javascript для того, чтобы заставить его работать.
Вишал Шетти
13

Это может и, вероятно, должно быть обработано с помощью ссылок :

«... вы можете использовать ReactDOM.findDOMNode в качестве« escape-люка », но мы не рекомендуем его, поскольку он нарушает инкапсуляцию и почти в каждом случае есть более ясный способ структурировать ваш код в модели React».

Пример кода:

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

    render() {
        return <div ref={(ref) => this._div = ref} />
    }
}
GGAlanSmithee
источник
Это прекрасно работает. Спасибо. Чтобы быть ясным, я поместил <div ref={(ref) => this._div = ref} />в самом первом <div>из моих заявлений рендеринга. Остальная часть моего рендера остается точно такой же.
Josh F
Если вы используете компоненты Styled, вам нужно будет использовать «innerRef» вместо «ref». Отличное решение
furcicm
Полностью работает. Для того, над чем я работал, я мог бы быть еще проще, <div ref="main">а потомthis.refs.main.scrollTop=0
chuckfactory
2
@chuckfactory установка ссылок с использованием строк, вероятно, будет удалена в какой-то момент, и на самом деле у нее есть некоторые интересные недостатки, о которых вы, возможно, захотите узнать. news.ycombinator.com/edit?id=12093234
NJensen
10

Вы можете сделать это в маршрутизаторе следующим образом:

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)}Положить сверху прокрутки. Для получения дополнительной информации проверьте: ссылка codepen

Ана Мария Кабрера
источник
элегантное решение, которое требует лишь небольшого изменения кода в маршрутизаторе, а не того, чтобы каждый компонент обрабатывал его сам. <3
Аленгель
К сожалению, onUpdate срабатывает с каждым новым маршрутомParam, маршрутизируемым по данному маршруту. Так что, если у вас, например, есть страница с кучей изображений, и если вы можете развернуть изображение в модальном режиме при нажатии, изменив маршрут на /somePage/:imgId, он прокрутится вверх :(. Любой способ «контролировать», запускать или нет событие onUpdate для определенных маршрутов / параметров?
connected_user
Когда я попробовал это, TypeScript пожаловался, что onUpdateего нет в подпорках HashRouter ... Если кто-то сталкивался с той же проблемой: я закончил тем, что использовал решение ScrollToTop, описанное ниже (и в документах о реагирующем маршрутизаторе), которое отлично сработало для меня.
Николь
9

Для тех, кто использует хуки, будет работать следующий код.

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

Обратите внимание, вы также можете импортировать useEffect напрямую: import { useEffect } from 'react'

Powderham
источник
1
[]В качестве второго средства параметров будет происходить только на первый рендер, вы пробовали без?
Powderham
8

Вот еще один подход, который позволяет вам выбрать, к каким смонтированным компонентам вы хотите восстановить положение прокрутки окна без массового дублирования ComponentDidUpdate / ComponentDidMount.

В приведенном ниже примере компонент Blog оборачивается функцией ScrollIntoView (), поэтому, если маршрут изменяется при монтировании компонента Blog, компонент ComponentDidUpdate для HOC обновит позицию прокрутки окна.

Вы можете так же легко обернуть его по всему приложению, чтобы при любом изменении маршрута он вызывал сброс окна.

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

Приведенный выше пример прекрасно работает, но если вы перешли на react-router-dom, то вы можете упростить вышеприведенное, создав объект, HOCкоторый оборачивает компонент.

Еще раз, вы также можете так же легко обернуть его на маршрутах (только изменения componentDidMountметода к componentDidUpdateметоду примера кода , написанного выше, а также обертывание ScrollIntoViewсwithRouter ).

контейнеры / ScrollIntoView.js

import { PureComponent, Fragment } from "react";

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

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

export default ScrollIntoView;

компоненты / 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 дает мне следующую ошибку «неиспользуемое выражение, ожидание назначения или вызова функции»
EX0MAK3R
@ EX0MAK3R - Обновлен ответ.
Мэтт Карлотта
7

Это единственное, что сработало для меня (с компонентом класса ES6):

componentDidMount() {
  ReactDOM.findDOMNode(this).scrollIntoView();
}
danharsanyi
источник
Точно так же. Я перепробовал все остальные решения, и это единственное, что сработало для меня.
Эрик Джеймс Роблес
7

Я использую ScrollToTop Component реагирует на маршрутизатор, код которого описан в документации по реагирующему маршрутизатору.

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

Я изменяю код в одном файле маршрутов, и после этого нет необходимости менять код в каждом компоненте.

Пример кода -

Шаг 1 - создайте компонент 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)

Шаг 2 - В файле App.js добавьте компонент ScrollToTop после <Router

const App = () => (
  <Router>
    <ScrollToTop>
      <App/>
    </ScrollToTop>
  </Router>
)
Arpit
источник
действительно хорошее решение! если у вас есть маршруты, просто визуализируйте их в верхней части маршрута, но ниже Router. Мне не нужно было менять каждый компонент.
сыпь
5

Крюковое решение :

  • Создать хук 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);
  • Оберните ваше приложение этим

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

Документация: https://reacttraining.com/react-router/web/guides/scroll-restoration

Квентин С
источник
5

Использование хуков в функциональных компонентах, при условии, что компонент обновляется при обновлении в результате.

import React, { useEffect } from 'react';

export const scrollTop = ({result}) => {
  useEffect(() => {
    window.scrollTo(0, 0);
  }, [result])
}
Габриэль Эзенванкво
источник
ru.reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook Не забывайте, что имя крючка должно начинаться со словаuse
CrsCaballero
4

Все вышеперечисленное не сработало для меня - не знаю почему, но:

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

работал, где HEADER - это идентификатор моего элемента заголовка

Джеймс Шизтар
источник
Я использовал хук useEffect, но он отлично работал для меня в проекте Gatsby. Спасибо!
jj0b
4

Если все хотят сделать что-то простое, вот решение, которое будет работать для всех

добавить эту мини функцию

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

вызовите функцию следующим образом из нижнего колонтитула страницы

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

если вы хотите добавить хорошие стили здесь 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
источник
фрагмент кода не работает. Но решение сработало для меня. Спасибо и ура!
Весь мир
3

Я использую React Hooks и хотел что-то повторно используемое, но также и то, что я мог бы вызвать в любое время (а не только после рендера).

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

Затем для использования крючка вы можете сделать:

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
источник
Отличное решение!
Ник
2

Если вы делаете это для мобильных устройств , по крайней мере, с хромом, вы увидите белую полосу внизу.

Это происходит, когда строка URL исчезает. Решение:

Измените CSS для высоты / минимальной высоты: 100% на высоту / минимальной высоты: 100vh .

Документы для разработчиков Google

Артур Карвалью
источник
1

Ни один из приведенных выше ответов в настоящее время не работает для меня. Оказывается, это .scrollToне так широко совместимо, как.scrollIntoView .

В нашем App.js, в componentWillMount()мы добавили

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

Это единственное решение, которое работает для нас повсеместно. root - это идентификатор нашего приложения. «Плавное» поведение не работает на каждом браузере / устройстве. Тайм-аут 777 немного консервативен, но мы загружаем много данных на каждую страницу, поэтому при тестировании это было необходимо. Короче 237 может работать для большинства приложений.

Тодд
источник
1

Я столкнулся с этой проблемой, создав сайт с Gatsby, чья ссылка построена поверх Reach Router. Кажется странным, что это изменение, которое должно быть сделано, а не поведение по умолчанию.

В любом случае, я попробовал многие из приведенных выше решений, и единственное, что на самом деле мне помогло, было:

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

Я поместил это в useEffect, но вы могли бы так же легко поместить его в componentDidMount или вызвать его любым другим способом.

Не уверен, почему window.scrollTo (0, 0) не будет работать для меня (и других).

jj0b
источник
0

Все решения говорят о добавлении прокрутки componentDidMountилиcomponentDidUpdate с DOM.

Я сделал все это и не работал.

Итак, выяснил какой-то другой способ, который прекрасно работает для меня.

componentDidUpdate() { window.scrollTo(0, 0) } В шапке добавлено , что у меня нет <Switch></Switch>элемента. Просто бесплатно в приложении. Работает.

Я также нашел о некоторых ScrollRestoration что , но сейчас я ленив. А пока оставим это в «DidUpdate».

Надеюсь, поможет!

быть в безопасности

Сезон Buzzcut
источник
0

Этот код вызывает плавное поведение на свитке :

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

Вы можете передавать другие параметры внутри scrollIntoView (). Можно использовать следующий синтаксис:

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

alignToTop Необязательный Является логическим значением:

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 Необязательно: Объект со следующими свойствами:

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

Более подробную информацию можно найти здесь: MDN документы

Абхай Широ
источник
0

Ни один из приведенных выше ответов в настоящее время не работает для меня. Оказывается, это .scrollToне так широко совместимо, как.scrollIntoView .

В нашем App.js, в componentWillMount()мы добавили

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

Это единственное решение, которое работает для нас повсеместно. rootэто идентификатор нашего приложения. «Плавное» поведение не работает на каждом браузере / устройстве. Тайм-аут 777 немного консервативен, но мы загружаем много данных на каждую страницу, поэтому при тестировании это было необходимо. Короче 237 может работать для большинства приложений.

Тодд
источник
0

Если я предполагаю, что вы воспроизводите главу, скажем, книгу на странице, все, что вам нужно сделать, это добавить это в свой код. Это сработало для меня как магия.

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

При этом вам не нужно создавать ссылку на визуализируемый компонент.

Ава Дьедоне
источник
0

Вот что я сделал:

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

       return(
         <div ref={ref}>
           ...
         </div>
        )
...
реагировать разработчик
источник
0

Вы можете использовать, это работает для меня.

import React, { useEffect } from 'react';

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

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

}, []);
bellabelle
источник
-1

Как-то так у меня работало на компоненте:

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

Тогда в какой бы функции ни работал с обновлениями:

this.refs.scroller.scrollTop=0
kojow7
источник
-1

Ничто не сработало для меня, но:

componentDidMount(){

    $( document ).ready(function() {
        window.scrollTo(0,0);
    });
}
gal007
источник