Scroll restore to top on navigation change
I am having a problem with scrolling, when navigating using react-router.
When I am navigating to a different component it is keeping the scroll position from the last navigation. However I want it to restore and start from the top whenever the navigation is changed.
class App extends Component{
render() {
return(
<BrowserRouter onUpdate={() => window.scrollTo(0, 0)} history={createBrowserHistory()}>
<div style={{display: 'flex'}}>
<div className='navBar'>
<ul style={{listStyleType: 'none'}}>
<li><Link to='/'>Home</Link></li>
<li><Link to='/towns'>Towns</Link></li>
<li><Link to='/pubs'>Pubs</Link></li>
<li><Link to='/venues'>Venues</Link></li>
<li><Link to='/cinemas'>Cinemas</Link></li>
</ul>
</div>
<div style={{flex:1, padding:'0px'}}>
{routes.map((route) => (
<Route
key={route.path}
path={route.path}
exact={route.exact}
component={route.main}
/>
))}
</div>
</div>
</BrowserRouter>
)
}
}
However it doesn't work. I have a feeling the issue is with my style of navigation rather then the window.scrollTo() snippet.
To my knowledge, since react-router v4, BrowserRouter
doesn't have an onUpdate
function.
Several ways to accomplish it. This will be the easiest...
components/ScrollIntoView.js
import { PureComponent } from "react";
import { withRouter } from "react-router-dom";
class ScrollIntoView extends PureComponent {
componentDidMount = () => window.scrollTo(0, 0);
componentDidUpdate = prevProps => {
if (this.props.location.pathname !== prevProps.location.pathname) window.scrollTo(0, 0);
};
render = () => this.props.children;
}
export default withRouter(ScrollIntoView);
routes/index.js
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Links from "../components/Links";
import Home from "../components/Home";
import About from "../components/About";
import Contact from "../components/Contact";
import ScrollIntoView from "../components/ScrollIntoView";
export default () => (
<BrowserRouter>
<div>
<ScrollIntoView>
<Links />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
<Links />
</ScrollIntoView>
</div>
</BrowserRouter>
);
Or you can find another approach here: Scroll to the top of the page after render in react.js
My solution using React hooks
import { useEffect, useRef } from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
const ScrollIntoView = ({ children, location }) => {
const prevLocation = useRef();
useEffect(() => {
if (prevLocation.current !== location.pathname) {
window.scrollTo(0, 0);
prevLocation.current = location.pathname;
}
}, [location]);
return children;
};
ScrollIntoView.propTypes = {
children: PropTypes.node,
location: PropTypes.object
};
export default withRouter(ScrollIntoView);