react-router: Persist location state

Out of the box, single-page applications built with react-router don’t persist data when navigating back and forward in the browser history. While this may be desirable at times, most of the time it greatly hurts the usability of the application and makes it feel clunky compared to regular web pages.

To make your application persists its state when navigating back and forward, you can store the relevant state in the history state.

The below example is built for react-router version 5, but is easily applicable for 6 as well.

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

export const useLocationState = <T = unknown>(): [T | null, (newState: T | null) => void] => {
  const history = useHistory<T | null>();
  const locationState: T | null = history.location.state ?? null;

  const setLocationState = (newState: T | null) =>
    history.replace({
      ...history.location,
      state: newState,
    });

  const clearLocationState = () => {
    history.replace({
      ...history.location,
      state: null,
    });
  };

  useEffect(() => {
    window.addEventListener("beforeunload", clearLocationState);

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

  return [locationState, setLocationState];
};