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

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

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

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

41 ответ

Решение

Я решил эту проблему, добавив:

componentDidUpdate() {
  ReactDOM.findDOMNode(this).scrollTop = 0
},

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

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

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

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

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

  • Создайте перехватчик 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

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

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

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

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

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

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

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

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

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

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)} положите верхнюю часть прокрутки. Для получения дополнительной информации проверьте: http://codepen.io/Yuschick/post/react-reset-scroll-position-when-changing-routes

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

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

Пример кода:

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

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

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

import React, { useEffect } from 'react';

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

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

}, []);

Я пробовал решение @sledgeweight, но оно не работает для некоторых представлений. Но добавление setTimeout работает отлично. На случай, если кто-то столкнется с той же проблемой, что и я. Ниже мой код.

      import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'

const ScrollToTop = () => {
    const { pathname } = useLocation()
    useEffect(() => {
        console.log(pathname)
        /* settimeout make sure this run after components have rendered. This will help fixing bug for some views where scroll to top not working perfectly */
        setTimeout(() => {
            window.scrollTo({ top: 0, behavior: 'smooth' })
        }, 0)
    }, [pathname])
    return null
}

export default ScrollToTop

Используйте его в AppRouter.js как

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

Это решение работает как с функциональным компонентом, так и с базой классов.

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

Шаг №1: Создайте функцию для ScrollToTop

      const scrollToTop = () => {
    window.scrollTo({
        top: 0,
        behavior: "smooth",
    });
};

Шаг № 2: вызовите эту функцию на event например onClick

      onRowClick={scrollToTop()}
// onClick={scrollToTop()}
// etc...

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

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

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

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

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

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

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

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

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

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

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

Я использую 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>
)

Я делал SPA в React 17.0, используя функциональные компоненты и window.scroll, window.scrollTo, и все эти варианты у меня не работают. Поэтому я решил использовать хук useRef. Я создал тег span в верхней части компонента с помощью Ref, а затем использовал и применил с помощью ref.current.scrollIntoView()

Вот небольшой пример:

      import React, { useEffect,useRef} from 'react';

export const ExampleComponent = () => {

  const ref = useRef();

  useEffect(() => {
      ref.current.scrollIntoView()
  }, []);

return(

 <>
   <span ref={ref}></span>
   <YourCodeHere />
   <MoreCode />
</>

)}

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

      useEffect(() => {
  window.scrollTo({top: 0, left: 0, behavior: 'smooth' });
}, [])

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

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

Страница, которая появляется после нажатия, просто напишите на ней.

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

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

import React, { useEffect } from 'react';

export const scrollTop = ({result}) => {
  useEffect(() => {
    window.scrollTo(0, 0);
  }, [result])
}

Для React v18+ я рекомендую использовать компонент-оболочку, это будет самый простой способ выполнения.

Шаг 1: Создайте компонент ScrollToTop (component/ScrollToTop.js)

      import { useEffect } from "react";
import { useLocation } from "react-router-dom";

export function ScrollToTop() {
  const { pathname } = useLocation();

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

  return null;
}

Шаг 2: Оберните им свое приложение (index.js)

      <React.StrictMode>
  <BrowserRouter>
    <ScrollToTop />
    <App />
  </BrowserRouter>
</React.StrictMode>

Объяснение: При каждом изменении пути будет вызываться useEffect для прокрутки страницы вверх.

Все перепробовал, но это единственное, что сработало.

 useLayoutEffect(() => {
  document.getElementById("someID").scrollTo(0, 0);
 });

Похоже, что все примеры useEffect не учитывают вас, возможно, вы захотите активировать это с помощью изменения состояния.

      const [aStateVariable, setAStateVariable] = useState(false);

const handleClick = () => {
   setAStateVariable(true);
}

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

Вы можете просто использовать это:

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

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

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

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

Некоторое время у меня была такая же проблема. Добавление window.scrollTo(0, 0); на каждую страницу болезненно и излишне. Итак, я добавил HOC, который обернет все мои маршруты и останется внутри компонента BrowserRouter:

       <ScrollTop>
    <Routes />
  </ScrollTop>

Внутри ScrollTopComponent у нас есть следующее:

      import React, { useEffect } from "react";
import { useLocation } from "react-router-dom";

const ScrollTop = (props) => {
  const { children } = props;

  const location = useLocation();

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

  return <main>{children}</main>;
};

export default ScrollTop;

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

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

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

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

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

Я использую 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>
}

Решение для функционального компонента - Использование хука useEffect()

       useEffect(() => {
window.history.scrollRestoration = 'manual';}, []);

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

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

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

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

Другие вопросы по тегам