在react.js中渲染后滚动到页面顶部


165

我有一个问题,我不知道如何解决。在我的react组件中,我在底部显示一长串数据和少量链接。单击任何此链接后,我将在列表中添加新的链接集合,并需要滚动到顶部。

问题是- 呈现新集合如何滚动到顶部?

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

Answers:


326

最后..我用过:

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设置为height或min-height时,我有时仍会遇到此问题:100%。我必须将其移除,或者将其包装在父级中,或者进一步移动到树中仍可以滚动的树中
消旋的

2
这对我有用,但在componentDidMount下不起作用,因为状态更改导致页面重新呈现时,可能不会触发CDM。所以把这个调用-window.scrollTo(0,0); -无论您在哪里,都可以更改状态。
Puneet Lamba

4
对于那些使用钩子的人,以下代码将起作用。 React.useEffect(() => { window.scrollTo(0, 0); }, []); 注意,您也可以直接导入useEffect:import { useEffect } from 'react'
Powderham

72

由于原始解决方案是为react的早期版本提供的,因此这里进行了更新:

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您可能正在react15上?尝试导入ReactDOM并执行 ReactDOM.findDOMNode(this).scrollTop = 0
Marcus Ericsson

12
this is undefined in arrow functions是不正确的。此关键字与封闭函数developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Joe Delgado

如果可能的话,你应该避免ReactDom.findDOMNode()。请改用ref。我在这里使用平滑滚动
bbrinx

default.a.createRef不是函数
Anupam Maurya

48

您可以使用类似这样的东西。ReactDom用于react.14。否则就做出反应。

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

对于React 16+,更新5/11/2019

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


在此页面上的所有建议中,这是唯一对我有用的建议。
Josh F

注意:如果componentDidUpdate对您不起作用,componentDidMount则是另一种选择。
Alex Fallenstedt

findDOMNode是用于访问基础DOM节点的转义口。在大多数情况下,不建议使用此逃生阴影,因为它会穿透组件抽象。在StrictMode中已弃用。reactjs.org/docs/react-dom.html
Vivek Kumar,

16

在React Routing中,存在一个问题,如果我们重定向到新路由,那么它将不会自动将您带到页面顶部。

即使我确实有同样的问题。

我只是将单行添加到我的组件中,它就像黄油一样工作。

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

参考:反应训练


如果我将其用于“跳到顶部”按钮,这是推荐的方法吗?还是有一种不使用window对象的“反应”方式?
Toxnyc

1
感谢您的注意,我给出的解决方案适用于v5以下版本的react-router dom版本,我使用的是v4.2.2,在那里,当您导航到另一个页面时,默认情况下不会被带到页面,因此我们必须在导航后手动将用户带到页面顶部,但是使用v5.0.1 react-router dom时,开箱即用的滚动恢复已退出,因为根据他们的文档,他们说浏览器开始支持默认情况下,此功能以及最新版本的react-router-dom导航后将带您到页面顶部。
维沙尔·谢蒂

1
@Toxnyc,所以使用窗口对象就是Javascript,如果React位于Javascript之上,那么即使您在幕后使用任何React插件,它也将仅使用Javascript和window对象,据我所知React文档没有任何可以获取窗口屏幕详细信息的方法。我们必须使用Javascript才能使其正常工作。
维沙尔·谢蒂

13

这可以并且可能应该使用refs处理:

“ ...您可以将ReactDOM.findDOMNode用作“逃生舱口”,但我们不建议您这样做,因为它破坏了封装,并且在几乎每种情况下,都有一种更清晰的方法可以在React模型中构造代码。”

示例代码:

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

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

这很好。谢谢。为了清楚起见,我将放在我的render语句的<div ref={(ref) => this._div = ref} />第一<div>行。我渲染的其余部分保持不变。
乔什·F

如果您使用样式化组件,则需要使用“ innerRef”而不是“ ref”。很好的解决方案
furcicm '18

完全可以。对于我正在从事的工作,我可能<div ref="main">this.refs.main.scrollTop=0
会变

2
使用字符串的@chuckfactory设置引用可能会在某个时候被删除,并且实际上存在一些您可能想了解的有趣的缺点。news.ycombinator.com/edit?id=12093234
新泽西州詹森,

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
阿伦格尔'17

不幸的是,onUpdate会在给定路由中路由的每个新路由Param触发。因此,例如,如果您的页面上有一堆图像,并且如果您可以在单击时将模式更改为时将图像扩展为/somePage/:imgId,则它将向上滚动:(。任何“控制”是否触发的方法?在具体路线/ PARAMS中的onUpdate事件
connected_user

当我尝试此操作时,TypeScript抱怨onUpdateHashRouter的道具中不存在……如果有人遇到相同的问题:我最终使用了下面进一步介绍的ScrollToTop解决方案(以及react-router文档),该解决方案对我来说非常理想。
妮可'18

8

这是另一种方法,它允许您选择要将窗口滚动位置重置为哪些已安装的组件,而无需大量复制ComponentDidUpdate / ComponentDidMount。

下面的示例使用ScrollIntoView()包装Blog组件,以便在安装Blog组件时更改路由时,HOC的ComponentDidUpdate将更新窗口滚动位置。

您可以轻松地将其包装在整个应用程序中,以便在任何路线更改时都将触发窗口重置。

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上面写的方法示例代码,以及包装ScrollIntoViewwithRouter)。

容器/ 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-更新答案。
马特·卡洛塔

8

对于那些使用钩子的人,以下代码将起作用。

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

注意,您也可以直接导入useEffect: import { useEffect } from 'react'


[]作为第二个参数意味着它只会发生在第一渲染,你试过没有?
Powderham

7

这是唯一对我有用的东西(带有ES6类组件):

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

同样。我尝试了所有其他解决方案,这是唯一对我有用的解决方案。
Erik James Robles

7

我正在使用react-router ScrollToTop组件,该代码在react-router文档中描述

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

我在单个Routes文件中更改代码,此后无需在每个组件中更改代码。

示例代码-

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

真的是一个很好的解决方案!如果您有路由,只需将其呈现在路由的顶部,但在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



4

以上所有内容都不适合我-不知道为什么,但是:

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

工作,其中HEADER是我的标头元素的ID


我使用了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;
}


该代码段似乎不起作用。但是解决方案对我有用。谢谢和欢呼!
globefire

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

很好的解决方案!
尼克


1

以上答案均不适用于我。事实证明,.scrollTo它不如广泛兼容.scrollIntoView

在我们的App.js中,componentWillMount()我们添加了

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

这是对我们通用的唯一解决方案。root是我们应用的ID。“平滑”行为并非在所有浏览器/设备上都有效。777超时有些保守,但是我们在每个页面上加载了大量数据,因此通过测试是必要的。较短的237可能适用于大多数应用程序。


1

我遇到了一个问题,那就是用Gatsby构建一个站点,该站点的链接建立在Reach Router的顶部。这是必须进行的修改而不是默认的行为,这似乎很奇怪。

无论如何,我尝试了上面的许多解决方案,唯一对我有用的解决方案是:

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

我将其放在useEffect中,但您也可以将其轻松放入componentDidMount或以任何其他方式触发它。

不知道为什么window.scrollTo(0,0)对我(和其他人)不起作用。


0

所有解决方案都涉及在DOM 上componentDidMountcomponentDidUpdate与DOM一起添加滚动条。

我做了所有这些,但没有成功。

因此,想出了一些对我来说很好的方法。

componentDidUpdate() { window.scrollTo(0, 0) } 在标题上添加 的是,该<Switch></Switch>元素不在元素中。只需在应用程序中免费。作品。

我还发现了一些ScrollRestoration的内容,但是现在我很懒。现在,将其保留为“ DidUpdate”方式。

希望能帮助到你!

注意安全


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是我们应用的ID。“平滑”行为并非在所有浏览器/设备上都有效。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)

}, []);

-1

这样的事情对我来说在组件上起作用:

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

然后在处理更新的任何函数中:

this.refs.scroller.scrollTop=0

-1

对我没有任何帮助,但:

componentDidMount(){

    $( document ).ready(function() {
        window.scrollTo(0,0);
    });
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.