5

When I change pages, the application is being kept at the same point it was on the previous page. I want to show the component from the top when I change pages. To achieve that, I am trying to implement React Router ScrollToTop.

I found the documentation and implemented it, but I am using react router v6, so it is a bit different.

https://v5.reactrouter.com/web/guides/scroll-restoration

Everything inside the ScrollToTop component doesn't get rendered, and I end up with a blank page.

App.js:

import { Router, Routes, Route } from "react-router-dom";
import './App.scss';
import Main from './pages/Main';
import Projects from './pages/Projects';
import NavBar from './components/NavBar';
import Footer from './components/Footer';
import ScrollToTop from './components/scrollToTop';

function App() {
  return (
    <div className="app" id="app">
      <NavBar /> 
        <div className='app-body'>
          <Router>
            <ScrollToTop>
              <Routes>
                <Route path="/portfolio" element={<Main />} />
                <Route path="/portfolio/projects" element={<Projects />} />
              </Routes>
            </ScrollToTop>
          </Router>
        </div>
      <Footer />
    </div>
  );
}

export default App;

ScrollToTop.js:

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

export default function ScrollToTop() {
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  return null;
}

3 Answers3

5

As others have pointed out, you are wrapping your Routes component with the ScrollToTop component, but instead of editing it to render its implicit children prop I suggest converting it to a React hook, especially considering since it doesn't actually render anything, you want it to run as a side-effect of navigation.

function useScrollToTop() {
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);
}

...

function App() {
  useScrollToTop();
  return (
    <div className="App">
      <div className="app-body">
        <NavBar />
        <Routes>
          <Route path="/portfolio" element={<Main />} />
          <Route path="/portfolio/projects" element={<Projects />} />
        </Routes>
      </div>
    </div>
  );
}

This necessarily requires you to lift the Router higher in the ReactTree to wrap the App component so it has a routing context to use for the useScrollToTop hook.

const rootElement = document.getElementById("root");
ReactDOM.render(
  <StrictMode>
    <Router>
      <App />
    </Router>
  </StrictMode>,
  rootElement
);
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
1

You put the Routes component as a descendant of ScrollToTop component, so you should return children instead of null.

ScrollToTop.js:

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

export default function ScrollToTop({ children }) {
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  return children;
}
Lin Du
  • 88,126
  • 95
  • 281
  • 483
1

The blank page is because you are returning null from <ScrollToTop> component. Instead if you return <></> or take the {children} prop and return that from <ScrollToTop >, it should work :)

Sanjay
  • 551
  • 6
  • 14