我有一个问题,当导航到另一个页面,它的位置将保持像之前的页面。所以它不会自动滚动到顶部。 我也试过使用window。onChange路由器上的scrollTo(0,0)。我还使用了scrollBehavior来修复这个问题,但它没有工作。对此有什么建议吗?


当前回答

2021年8月,

而不是在每个页面都这样做,你可以在App.js中这样做

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

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

在useEffect中设置位置将确保每次路径更改时滚动到顶部。

其他回答

我写了一个叫withScrollToTop的高阶组件。这个HOC有两个标志:

onComponentWillMount -导航时是否滚动到顶部(componentWillMount) onComponentDidUpdate—是否在更新时滚动到顶部(componentDidUpdate)。该标志在组件没有卸载但发生导航事件的情况下是必要的,例如,从/users/1到/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));
};

使用它:

import withScrollToTop from './withScrollToTop';

function MyComponent() { ... }

export default withScrollToTop(MyComponent);

这个答案只适用于v4版本,而不适用于后续版本。

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)

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

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

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

^直接从文档中复制

显然,这适用于大多数情况,但是关于如何处理选项卡接口以及为什么没有实现通用解决方案还有更多内容。

使用useEffect() -功能组件的解决方案

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

这是另一种方法。

对于react-router v4,您还可以以以下方式将侦听器绑定到历史事件中的change:

let firstMount = true;
const App = (props) => {
    if (typeof window != 'undefined') { //incase you have server-side rendering too             
        firstMount && props.history.listen((location, action) => {
            setImmediate(() => window.scrollTo(0, 0)); // ive explained why i used setImmediate below
        });
        firstMount = false;
    }

    return (
        <div>
            <MyHeader/>            
            <Switch>                            
                <Route path='/' exact={true} component={IndexPage} />
                <Route path='/other' component={OtherPage} />
                // ...
             </Switch>                        
            <MyFooter/>
        </div>
    );
}

//mounting app:
render((<BrowserRouter><Route component={App} /></BrowserRouter>), document.getElementById('root'));

如果点击链接改变了路由,那么没有setimmediation(),滚动级别也会被设置为0,但是如果用户在浏览器上按下后退按钮,那么它将不起作用,因为浏览器在按下后退按钮时手动将滚动级别重置到上一个级别,所以通过使用setimmediation(),我们使我们的函数在浏览器完成重置滚动级别后执行,从而给我们提供了预期的效果。

我的解决方案:我在我的屏幕组件中使用的组件(我想要滚动到顶部)。

import { useLayoutEffect } from 'react';

const ScrollToTop = () => {
    useLayoutEffect(() => {
        window.scrollTo(0, 0);
    }, []);

    return null;
};

export default ScrollToTop;

这可以在返回时保留滚动位置。 使用useEffect()对我来说是有bug的,当返回文档时,文档会滚动到顶部,当已经滚动的文档中的路由被更改时,也会有闪烁的效果。