Skip to content
Snippets Groups Projects
useNavigate.ts 1.39 KiB
import { useCallback } from 'react';
import {
  type Location,
  type NavigateFunction,
  type NavigateOptions,
  type To,
  useLocation,
  useNavigate as useNavigateReactRouter,
} from 'react-router-dom';

export function useNavigate(): NavigateFunction {
  const location = useLocation();
  const navigate = useNavigateReactRouter();
  return useCallback(
    (to: To | number, options: NavigateOptions = {}) => {
      if (typeof to === 'number') {
        navigate(to);
      } else {
        const optionsWithPrevLocation: NavigateOptions = {
          replace:
            options.replace || isSamePath(to, location) ? true : undefined,
          ...options,
          state: {
            ...options?.state,
            previousLocation: location,
          },
        };

        const { previousLocation, ...previousOriginalState } =
          location.state || {};

        if (
          previousLocation == null ||
          !isSamePath(to, previousLocation) ||
          JSON.stringify(options?.state || {}) !==
            JSON.stringify(previousOriginalState)
        ) {
          navigate(to, optionsWithPrevLocation);
        } else {
          // `to` is the same as the previous location. Just go back.
          navigate(-1);
        }
      }
    },
    [navigate, location],
  );
}

function isSamePath(to: To, location: Location) {
  return to === location.pathname + location.search + location.hash;
}