Untitled
raw download clone
TSX
views 16
,
size 2907 b
import React, { Fragment, useEffect, useState } from 'react';
import { Prompt, useHistory } from 'react-router-dom';
import { ConfirmationModal } from 'components/ConfirmationModal';
import { ApiConfig } from '../../api-config';

type Location = {
  hash: string;
  pathname: string;
  search: string;
};

type Props = {
  when: boolean; // When should shouldBlockNavigation be invoked
  navigate?: (path: string) => void; // Navigate function
  shouldBlockNavigation?: (nextLocation: Location) => boolean; // Use as "message" prop of Prompt of React-Router
  onBeforeNavigate?: () => void;
};

const isDev = ApiConfig.isDevelopmentEnv() && process.env.REACT_APP_DISABLE_ROUTE_LEAVING_GUARD;

const confirmationMessage = 'При уходе со страницы несохраненные данные будут потеряны. Подтвердить?';

export const RouteLeavingGuard: React.FC<Props> = ({
  when,
  navigate,
  shouldBlockNavigation = () => true,
  onBeforeNavigate = () => {},
}) => {
  const [lastLocation, setLastLocation] = useState<Location | null>(null);
  const [isNavigationConfirmed, setIsNavigationConfirmed] = useState(false);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const actualWhen = !isDev && when;

  const history = useHistory();

  const showModal = (location: Location) => {
    setIsModalOpen(true);
    setLastLocation(location);
  };

  const closeModal = () => setIsModalOpen(false);

  const handleBlockedNavigation = (nextLocation: Location) => {
    if (!isNavigationConfirmed && shouldBlockNavigation(nextLocation)) {
      showModal(nextLocation);
      return false;
    }

    return true;
  };

  const handleConfirmNavigation = () => {
    setIsModalOpen(false);

    if (lastLocation) {
      setIsNavigationConfirmed(true);
    }
  };

  const handleOnBeforeUnload = (e: BeforeUnloadEvent) => {
    (e || window.event).returnValue = confirmationMessage;

    window.addEventListener('unload', () => {
      onBeforeNavigate();
    });

    return confirmationMessage;
  };

  useEffect(() => {
    window.onbeforeunload = actualWhen ? handleOnBeforeUnload : null;

    return () => {
      window.onbeforeunload = null;
    };
  });

  useEffect(() => {
    if (isNavigationConfirmed && lastLocation) {
      const nextLocation = `${lastLocation.pathname}${lastLocation.search}`;

      onBeforeNavigate();
      navigate ? navigate(nextLocation) : history.push(nextLocation);
    }
  }, [isNavigationConfirmed]);

  return (
    <Fragment>
      <Prompt when={actualWhen} message={handleBlockedNavigation} />
      <ConfirmationModal
        isModalOpen={isModalOpen}
        onConfirm={handleConfirmNavigation}
        onCancel={closeModal}
        message={confirmationMessage}
        noCloseIcon
      />
    </Fragment>
  );
};
close fullscreen
Login or Register to edit or fork this paste. It's free.