在 react.js 中呈现后滚动到页面顶部

Scroll to the top of the page after render in react.js

我有一个问题,我不知道如何解决。 在我的 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;

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

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

2019 年 5 月 11 日针对 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)
    }
  }

由于最初的解决方案是为 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

这可以而且可能应该使用 refs:

来处理

"... you can use ReactDOM.findDOMNode as an "escape hatch" but we don't recommend it since it breaks encapsulation and in almost every case there's a clearer way to structure your code within the React model."

示例代码:

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

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

最后..我用了:

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

编辑:React v16.8+

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

你可以在路由器中这样做:

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 link

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

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

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

下面的示例是用 ScrollIntoView() 包装 Blog 组件,这样如果在安装 Blog 组件时路由发生变化,那么 HOC 的 ComponentDidUpdate 将更新 window 滚动位置。

您可以轻松地将它包裹在整个应用程序中,这样在任何路线更改时,它都会触发 window 重置。

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

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

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

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 文件中,在 <Router

之后添加 ScrollToTop 组件
const App = () => (
  <Router>
    <ScrollToTop>
      <App/>
    </ScrollToTop>
  </Router>
)

以上所有对我都不起作用 - 不知道为什么但是:

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

有效,其中 HEADER 是我的 header 元素的 ID

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

连我也遇到了同样的问题。

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

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

参考:react training

如果您在 移动设备 上这样做,至少在 chrome 上,您会在底部看到一个白色条。

当 URL 条消失时会发生这种情况。解决方案:

Change the css for height/min-height: 100% to height/min-height: 100vh.

Google Developer Docs

此代码将在滚动上导致平滑行为

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

像这样的东西在组件上对我有用:

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

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

this.refs.scroller.scrollTop=0

所有解决方案都讨论了在 componentDidMountcomponentDidUpdate 上添加滚动条,但使用 DOM。

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

所以,想出了一些其他适合我的方法。

Added componentDidUpdate() { window.scrollTo(0, 0) } on the header, that mine is out of the <Switch></Switch> element. Just free in the app. Works.

我也发现了一些ScrollRestoration的东西,但是我现在很懒。现在将保持“DidUpdate”方式。

以上答案中的

None 目前对我有效。事实证明 .scrollTo 没有 .scrollIntoView.

广泛兼容

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

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

这是唯一对我们普遍适用的解决方案。 root 是我们App的ID。 "smooth" 行为并不适用于所有浏览器/设备。 777 超时有点保守,但是我们在每个页面加载大量数据,所以通过测试这是必要的。较短的 237 可能适用于大多数应用程序。

以上答案中的

None 目前对我有用。事实证明 .scrollTo 没有 .scrollIntoView.

广泛兼容

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

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

这是唯一对我们普遍适用的解决方案。 root 是我们 App 的 ID。 "smooth" 行为并不适用于所有浏览器/设备。 777 超时有点保守,但是我们在每个页面加载大量数据,所以通过测试这是必要的。较短的 237 可能适用于大多数应用程序。

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

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

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

我正在使用 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>
}

Hook解决方案:

  • 创建 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>

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

在功能组件中使用 Hooks, 假设组件在结果 props

有更新时更新
import React, { useEffect } from 'react';

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

我 运行 在这个问题中使用 Gatsby 构建了一个站点,其 Link 构建在 Reach Router 之上。这是必须进行的修改而不是默认行为,这似乎很奇怪。

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

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

我把它放在 useEffect 中,但你可以很容易地把它放在 componentDidMount 中,或者以你想要的任何其他方式触发它。

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

如果我假设您正在渲染一个章节,例如每页一本书,您需要做的就是将其添加到您的代码中。这对我来说就像魔法一样有效。

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

有了这个,你就不需要在正在渲染的组件上创建一个 ref。

如果所有想做的都是简单的事情,这里有一个适合所有人的解决方案

添加这个迷你功能

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

这是我做的:

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

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

对我没有任何作用,但是:

componentDidMount(){

    $( document ).ready(function() {
        window.scrollTo(0,0);
    });
}

这对我有用。

import React, { useEffect } from 'react';

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

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

}, []);

对于功能组件;

import React, {useRef} from 'react';
function ScrollingExample (props) {
// create our ref
const refToTop = useRef();

return (
<h1 ref={refToTop}> I wanna be seen </h1>
// then add enough contents to show scroll on page
<a onClick={()=>{
    setTimeout(() => { refToTop.current.scrollIntoView({ behavior: 'smooth' })}, 500)
        }}>  Take me to the element <a>
);
}

我尝试了所有方法,但这是唯一有效的方法。

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

我在 index.html 页面上添加了一个事件侦听器,因为所有页面加载和重新加载都是通过它完成的。以下是片段。

// Event listener
addEventListener("load", function () {
    setTimeout(hideURLbar, 0);
}, false);
  
function hideURLbar() {
    window.scrollTo(0, 1);
}

点击后出现的页面,写入即可。

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

有一段时间我也有同样的问题。添加 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;

看起来所有 useEffect 示例都没有考虑到您可能希望通过状态更改来触发它。

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

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

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

我在 React 17.0 中使用功能组件和 window.scroll、window.scrollTo 做一个 SPA,所有这些变体都不适合我。所以我使用 useRef 钩子做了一个解决方案。我使用 Ref 在组件顶部创建了一个 span 标签,然后使用 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() hook

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

此解决方案适用于功能组件以及 Class 基础。

首先,我不喜欢在每次重新渲染时滚动到顶部的想法,相反,我喜欢特定事件的附件功能。

第 1 步:创建 ScrollToTop 函数

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

第 2 步:在 event 例如 onClick

上调用此函数
onRowClick={scrollToTop()}
// onClick={scrollToTop()}
// etc...

我已经尝试过@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>