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

问题描述 投票:42回答:12

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

javascript reactjs react-router react-router-redux
12个回答
69
投票

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

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

^ copied directly from the documentation

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


0
投票

对于具有1-4个路由的较小应用程序,您可以尝试使用#id重定向到顶部DOM元素而不是路由来破解它。然后,不需要在ScrollToTop中包装路由或使用生命周期方法。


-1
投票

我发现react router dom scroll restoration正在工作。我把它放在ReactDOM.findDomNode(this).scrollIntoView()里面。


-8
投票

这是hacky(但有效):我只是添加

window.scrollTo(0,0);

渲染();


26
投票

这个答案是遗留代码,路由器v4 +检查其他答案

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

如果它不起作用,你应该找到原因。也在componentDidMount

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

你可以使用:

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

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

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

21
投票

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

为此,您可以创建装饰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);

componentDidUpdate上,我们可以检查位置路径名何时更改并将其与path道具匹配,如果满足,则恢复窗口滚动。

这种方法有什么好处,我们可以拥有恢复滚动的路由和不恢复滚动的路由。

这是一个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;

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

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


19
投票

但课程是如此2018年

ScrollToTop implementation with React Hooks

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

用法:

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

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

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

用法:

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

11
投票

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

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

const history = createBrowserHistory()

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

<Router history={history}>

7
投票

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

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

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

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

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

2
投票

我写了一个名为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);

1
投票

这是另一种方法。

对于react-router v4,您还可以通过以下方式绑定侦听器以更改历史记录事件:

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

如果通过单击链接更改路线但是如果用户在浏览器上按下后退按钮,那么滚动级别将设置为0而没有setImmediate(),那么当后退按钮是后退按钮时,浏览器将手动将滚动级别手动重置为上一级别按下,所以通过使用setImmediate()我们使我们的功能在浏览器完成后重置滚动级别执行,从而给我们所需的效果。


1
投票

使用React路由器dom v4

创建一个scrollToTopComponent组件,如下所示

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)

或者如果您使用标签,请使用下面的内容

class ScrollToTopOnMount extends Component {
    componentDidMount() {
      window.scrollTo(0, 0)
    }

    render() {
      return null
    }
}

class LongContent extends Component {
    render() {
      <div>
         <ScrollToTopOnMount/>
         <h1>Here is my long content page</h1>
      </div>
    }
}

// somewhere else
<Route path="/long-content" component={LongContent}/>

希望这有助于更多关于滚动恢复访问那里docs hare qazxsw poi

© www.soinside.com 2019 - 2024. All rights reserved.