4

I need to scroll to the top every time the URL changes in my project (and on page reload).

Everything is working but I am having a problem with the browser back button. Even if the pathname changes the page doesn't scroll to the top as it should, it works on all other cases (page reload and regular link navigation).

I tried to counter that by creating a custom hook just for the back button, but it's not behaving how I want. I also tried a bunch of other things, but nothing seems to work as for the browser back button

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

const ScrollToTop = () => {
  const { pathname } = useLocation();

  const useBackButton = () => {
    const [isBack, setIsBack] = useState(false);
    const handleEvent = () => {
      setIsBack(true);
    };
    useEffect(() => {
      window.addEventListener("popstate", handleEvent);
      return () => window.removeEventListener("popstate", handleEvent);
    });
    return isBack;
  };

  const backButton = useBackButton();
  console.log(backButton);

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

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

  return null;
};

export default ScrollToTop;
giosan
  • 291
  • 1
  • 4
  • 15
  • If you are using a single page React app, I wouldn't expect `onbeforeunload` to fire on page navigation, since you're not actually leaving the page. Something like this might be useful: [Detect Route Change with react-router](https://stackoverflow.com/questions/45373742/detect-route-change-with-react-router) – DBS Mar 29 '22 at 16:25
  • you can create a link and inside create a button and put the href=# – Elio Bteich Mar 29 '22 at 16:26
  • you are right, but that's just for page reload. The first useEffect is for navigation and backbutton – giosan Mar 29 '22 at 16:26
  • The url change is tracked with useLocation through the const pathname, but for some reason even if the url changes when pressing the browser back button the scrollTo(0 ,0) is not triggered – giosan Mar 29 '22 at 16:38

2 Answers2

4

I don't think the beforeunload event is necessary here, but will include it anyway. Instead of using an event listener for the "popstate" event you can use the useNavigationType hook to expressly check for a POP event type.

Note: Use window.addEventListener for the beforeunload event so you don't pollute/mutate the window object.

Example:

import { NavigationType, useLocation, useNavigationType } from "react-router-dom";

const useBackButton = () => {
  const navType = useNavigationType();
  return navType === NavigationType.Pop;
};

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

  const isPop = useBackButton();

  const scrollToTop = () => window.scrollTo(0, 0);

  useEffect(() => {
    scrollToTop();
  }, [pathname, isPop]);

  useEffect(() => {
    window.addEventListener("beforeunload", scrollToTop);
    return () => {
      window.removeEventListener("beforeunload", scrollToTop);
    };
  }, []);
};

Usage:

function App() {
  useScrollToTop();

  return (
    ...
  );
}

Edit scroll-to-top-on-browser-back-button-click-react

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
1

Here is my solution using React hooks:

const [showsScrolBtn, setShowScrolBtn] = useState(false);

useEffect(() => {
  const handleButtonVisibility = () => {
    window.pageYOffset > 300 ? setShowScrolBtn(true) : setShowScrolBtn(false);
  };

  window.addEventListener("scroll", handleButtonVisibility);
  return () => {
    window.addEventListener("scroll", handleButtonVisibility);
  };
}, []);

{
  showsScrolBtn && (
    <div
      id="scrolToTop"
      onClick={() => {
        window.scrollTo({
          top: 0,
          left: 0,
          behavior: "smooth",
        });
      }}
      style={{
        position: "fixed",
        bottom: "60px",
        right: "60px",
        color: "gray",
        textAlign: "center",
        cursor: "pointer",
        fontSize: "4em",
        lineHeight: 0,
        textDecoration: "none",
      }}
    >
      Click To Move Top
    </div>
  );
}
giosan
  • 291
  • 1
  • 4
  • 15
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 19 '22 at 19:53