Why does my react-router link bring me to the middle of a page?

Viewed 5685

I've got many links on my site but only one that does this.

Instead of bringing me to the top with the navbar, it just goes to the middle of the content. Any idea why this could be?

It is a page that has has a .map going through some JSON, which is rendering div elements of text down the page...if it matters.

Here is some code that might be relevant.

The Route

<Route exact path ="/reviews" component={Reviews} />

The Link

                <Link to="/reviews">
                <ViewAllBtn>View All</ViewAllBtn>
            </Link>

Landing page (lands in the middle instead of top)

        <MotherDiv>
        <NavBar />
        <Prompt>
            <PromptHead>Customer Reviews</PromptHead>
            <PromptBody>Heres what our loyal customers think of Heavenly Details.</PromptBody>
        </Prompt>
        <ReviewsDiv>
            {reviews.map(review =>
                <ReviewDiv key={review.name}>
                    <Name>{review.name}</Name>
                    <Body>{review.body}</Body>
                </ReviewDiv >
            )}
        </ReviewsDiv>
        <Footer />
    </MotherDiv>
4 Answers

react-router renders a new component when the URL changes instead of hard refreshing the page. This has a nasty side effect that it remembers your current scroll position and doesn't automatically scroll back to the top of the page when the page changes.

I guess by the sound of your question that this might be the problem here. If it is, than it's quite easy to counteract this issue with a simple higher order component:

class ScrollToTop extends Component {
  componentDidUpdate(prevProps) {
    const { location } = this.props;
    if (location.pathname !== prevProps.location.pathname) {
      window.scrollTo(0, 0);
    }
  }

  render() {
    const { children } = this.props;
    return children;
  }
}

export default withRouter(ScrollToTop);

Wrap this component around your App like this:

render(
  <ScrollToTop>
    <App />
  </ScrollToTop>,
  document.getElementById('app'),
);

And your app should now automatically reset it's scroll position when the route changes.

You can fix this by automatically scrolling to the top of the page each time you navigate, but this actually doesn't solve the underlying issue.

The problem

The the problem is that your outermost div is the element that is scrolling and when you navigate, you are only rendering the elements inside your already scrolled div, which is why your new page appears as though you've already scrolled part way down (because technically you have)

Here is a clean way to solve this...

Stop the scroll on the outer div

Add the overflowY style as 'hidden' on the main div in App.js

Enable scroll on each page

Wrap the individual pages in a div with the overflowY style set to 'scroll'. I usually make this its own functional component to easily wrap it around all the other pages

import React from 'react';

import './App.css'

import {
  BrowserRouter as Router,
  Switch,
  Route,
  withRouter
} from "react-router-dom";


function Page () {
  return (
    <div style={{overflowY: 'scroll'}}>
     {props.children}
    </div>
  )
}


function App() {

  return (
    <div className="App">
      <Router>
      <div id="main" style={{overflowY: 'hidden'}}>
      <Switch>
        <Route exact path="/screen1">
          <Page>
            <Screen1 />
          </Page>
        </Route>
        <Route path="/screen2">
          <Page>
            <Screen2 />
          </Page>
        </Route>
      </Switch>
      </div>
      </Router>
    </div>
  );
}

This way, each time you navigate to a new page, the scrolled div will be wiped out and replaced by a new div. Which results in you always starting at the top of the page.

Related