Javascript react-router 在每次转换时滚动到顶部

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36904185/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 19:42:01  来源:igfitidea点击:

react-router scroll to top on every transition

javascriptreactjsreact-routerreact-router-redux

提问by adrian hartanto

I have an issue when navigating into another page, its position will remain like the page before. So it wont scroll to top automatically. I've also tried to use window.scrollTo(0, 0)on onChange router. I've also used scrollBehavior to fix this issue but it didnt work. Any suggestion about this?

我在导航到另一个页面时遇到问题,它的位置将保持与之前的页面一样。所以它不会自动滚动到顶部。我也尝试window.scrollTo(0, 0)在 onChange 路由器上使用。我也使用 scrollBehavior 来解决这个问题,但它没有用。对此有何建议?

回答by mtl

The documentation for React Router v4 contains code samplesfor scroll restoration. Here is their first code sample, which serves as a site-wide solution for “scroll to the top” when a page is navigated to:

React Router v4 的文档包含滚动恢复的代码示例。这是他们的第一个代码示例,当页面被导航到时,它作为“滚动到顶部”的站点范围的解决方案:

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)

Then render it at the top of your app, but below Router:

然后在你的应用程序顶部渲染它,但在路由器下面:

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

// or just render it bare anywhere you want, but just one :)
<ScrollToTop/>

^ copied directly from the documentation

^ 直接从文档中复制

Obviously this works for most cases, but there is more on how to deal with tabbed interfaces and why a generic solution hasn't been implemented.

显然这适用于大多数情况,但还有更多关于如何处理选项卡式界面以及为什么尚未实施通用解决方案的内容。

回答by zurfyx

but classes are so 2018

但课程是如此 2018

ScrollToTop implementation with React Hooks

使用 React Hooks 实现 ScrollToTop

ScrollToTop.js

滚动到顶部.js

import { useEffect } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return (null);
}

export default withRouter(ScrollToTop);

Usage:

用法:

<Router>
  <Fragment>
    <ScrollToTop />
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </Fragment>
</Router>


ScrollToTop can also be implemented as a wrapper component:

ScrollToTop 也可以实现为一个包装组件:

ScrollToTop.js

滚动到顶部.js

import React, { useEffect, Fragment } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history, children }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

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

export default withRouter(ScrollToTop);

Usage:

用法:

<Router>
  <ScrollToTop>
    <Switch>
        <Route path="/" exact component={Home} />
    </Switch>
  </ScrollToTop>
</Router>

回答by Lukas Liesis

This answer is for legacy code, for router v4+ check other answers

此答案适用于遗留代码,适用于路由器 v4+ 检查其他答案

<Router onUpdate={() => window.scrollTo(0, 0)} history={createBrowserHistory()}>
  ...
</Router>

If it's not working, you should find the reason. Also inside componentDidMount

如果它不起作用,您应该找到原因。还有里面componentDidMount

document.body.scrollTop = 0;
// or
window.scrollTo(0,0);

you could use:

你可以使用:

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

you could add some flag like "scrolled = false" and then in update:

你可以添加一些像“scrolled = false”这样的标志,然后在更新中:

componentDidUpdate() {
  if(this.scrolled === false){
    window.scrollTo(0,0);
    scrolled = true;
  }
}

回答by Dragos Rizescu

For react-router v4, here is a create-react-app that achieves the scroll restoration: http://router-scroll-top.surge.sh/.

对于react-router v4,这是一个实现滚动恢复的 create-react-app:http://router-scroll-top.surge.sh/

To achieve this you can create decorate the Routecomponent and leverage lifecycle methods:

为此,您可以创建装饰Route组件并利用生命周期方法:

import React, { Component } from 'react';
import { Route, withRouter } from 'react-router-dom';

class ScrollToTopRoute extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.path === this.props.location.pathname && this.props.location.pathname !== prevProps.location.pathname) {
      window.scrollTo(0, 0)
    }
  }

  render() {
    const { component: Component, ...rest } = this.props;

    return <Route {...rest} render={props => (<Component {...props} />)} />;
  }
}

export default withRouter(ScrollToTopRoute);

On the componentDidUpdatewe can check when the location pathname changes and match it to the pathprop and, if those satisfied, restore the window scroll.

componentDidUpdate我们可以检查位置路径发生变化时,它匹配的path道具,如果那些满足,恢复窗口滚动。

What is cool about this approach, is that we can have routes that restore scroll and routes that don't restore scroll.

这种方法很酷的是,我们可以拥有恢复滚动的路由和不恢复滚动的路由。

Here is an App.jsexample of how you can use the above:

以下是App.js如何使用上述内容的示例:

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Lorem from 'react-lorem-component';
import ScrollToTopRoute from './ScrollToTopRoute';
import './App.css';

const Home = () => (
  <div className="App-page">
    <h2>Home</h2>
    <Lorem count={12} seed={12} />
  </div>
);

const About = () => (
  <div className="App-page">
    <h2>About</h2>
    <Lorem count={30} seed={4} />
  </div>
);

const AnotherPage = () => (
  <div className="App-page">
    <h2>This is just Another Page</h2>
    <Lorem count={12} seed={45} />
  </div>
);

class App extends Component {
  render() {
    return (
      <Router>
        <div className="App">
          <div className="App-header">
            <ul className="App-nav">
              <li><Link to="/">Home</Link></li>
              <li><Link to="/about">About</Link></li>
              <li><Link to="/another-page">Another Page</Link></li>
            </ul>
          </div>
          <Route exact path="/" component={Home} />
          <ScrollToTopRoute path="/about" component={About} />
          <ScrollToTopRoute path="/another-page" component={AnotherPage} />
        </div>
      </Router>
    );
  }
}

export default App;

From the code above, what is interesting to point out is that only when navigating to /aboutor /another-pagethe scroll to top action will be preformed. However when going on /no scroll restore will happen.

从上面的代码中,有趣的是,只有在导航到/about/another-page滚动到顶部动作时才会执行。但是,在进行时/不会发生滚动恢复。

The whole codebase can be found here: https://github.com/rizedr/react-router-scroll-top

整个代码库可以在这里找到:https: //github.com/rizedr/react-router-scroll-top

回答by Harry Le

It is noteable that the onUpdate={() => window.scrollTo(0, 0)}method is outdated.

值得注意的是,该onUpdate={() => window.scrollTo(0, 0)}方法已经过时。

Here is a simple solution for react-router 4+.

这是 react-router 4+ 的简单解决方案。

const history = createBrowserHistory()

history.listen(_ => {
    window.scrollTo(0, 0)  
})

<Router history={history}>

回答by Jhalaa Chinoy

I had the same issue with my application.Using the below code snippet helped me scroll to the top of the page on click of the next button.

我的应用程序遇到了同样的问题。使用下面的代码片段帮助我在单击下一个按钮时滚动到页面顶部。

<Router onUpdate={() => window.scrollTo(0, 0)} history= {browserHistory}>
...
</Router>

However, the issue still persisted on browser back. After a lot of trials, realized that this was because of the browser window's history object, which has a property scrollRestoration which was set to auto.Setting this to manual solved my problem.

但是,问题仍然存在于浏览器后面。经过大量试验,意识到这是因为浏览器窗口的历史对象,它有一个属性 scrollRestoration 被设置为 auto.Setting this to manual 解决了我的问题。

function scrollToTop() {
    window.scrollTo(0, 0)
    if ('scrollRestoration' in history) {
        history.scrollRestoration = 'manual';
    }
}

<Router onUpdate= {scrollToTop} history={browserHistory}>
....
</Router>

回答by Thomas Aumaitre

In a component below <Router>

在下面的组件中 <Router>

Just add a React Hook (in case you are not using a React class)

只需添加一个 React Hook(以防您不使用 React 类)

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

回答by Luis Febro

I want to share my solution for those who are using react-router-dom v5since none of these v4 solutions did the work for me.

我想为那些正在使用的人分享我的解决方案,react-router-dom v5因为这些 v4 解决方案都没有为我工作。

What solved my problem was installing react-router-scroll-topand put the wrapper in the <App />like this:

解决我的问题的是安装react-router-scroll-top并将包装器放在<App />这样的位置:

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

and that's it! it worked!

就是这样!有效!

回答by Kris Dover

Hooks are composable, and since React Router v5.1 we have a useHistory()hook. So based off @zurfyx's answer I've created a re-usable hook for this functionality:

钩子是可组合的,从 React Router v5.1 开始,我们有了一个useHistory()钩子。因此,基于@zurfyx 的回答,我为此功能创建了一个可重复使用的钩子:

// useScrollTop.ts
import { useHistory } from 'react-router-dom';
import { useEffect } from 'react';

/*
 * Registers a history listener on mount which
 * scrolls to the top of the page on route change
 */
export const useScrollTop = () => {
    const history = useHistory();
    useEffect(() => {
        const unlisten = history.listen(() => {
            window.scrollTo(0, 0);
        });
        return unlisten;
    }, [history]);
};

回答by Yangshun Tay

I wrote a Higher-Order Component called withScrollToTop. This HOC takes in two flags:

我编写了一个名为withScrollToTop. 这个 HOC 有两个标志:

  • onComponentWillMount- Whether to scroll to top upon navigation (componentWillMount)
  • onComponentDidUpdate- Whether to scroll to top upon update (componentDidUpdate). This flag is necessary in cases where the component is not unmounted but a navigation event occurs, for example, from /users/1to /users/2.
  • onComponentWillMount- 导航时是否滚动到顶部 ( componentWillMount)
  • onComponentDidUpdate- 更新时是否滚动到顶部 ( componentDidUpdate)。在组件未卸载但发生导航事件(例如 from /users/1to )的情况下,此标志是必需的/users/2


// @flow
import type { Location } from 'react-router-dom';
import type { ComponentType } from 'react';

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';

type Props = {
  location: Location,
};

type Options = {
  onComponentWillMount?: boolean,
  onComponentDidUpdate?: boolean,
};

const defaultOptions: Options = {
  onComponentWillMount: true,
  onComponentDidUpdate: true,
};

function scrollToTop() {
  window.scrollTo(0, 0);
}

const withScrollToTop = (WrappedComponent: ComponentType, options: Options = defaultOptions) => {
  return class withScrollToTopComponent extends Component<Props> {
    props: Props;

    componentWillMount() {
      if (options.onComponentWillMount) {
        scrollToTop();
      }
    }

    componentDidUpdate(prevProps: Props) {
      if (options.onComponentDidUpdate &&
        this.props.location.pathname !== prevProps.location.pathname) {
        scrollToTop();
      }
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
};

export default (WrappedComponent: ComponentType, options?: Options) => {
  return withRouter(withScrollToTop(WrappedComponent, options));
};

To use it:

要使用它:

import withScrollToTop from './withScrollToTop';

function MyComponent() { ... }

export default withScrollToTop(MyComponent);