3

I have code block like this

const onRouteChangeStart = React.useCallback(() => {
    if (formState.isDirty) {
      if (window.confirm('Confirmation message')) {
        return true;
      }
      NProgress.done();
      throw "Abort route change by user's confirmation.";
    }
  }, [formState.isDirty]);

  React.useEffect(() => {
    Router.events.on('routeChangeStart', onRouteChangeStart);

    return () => {
      Router.events.off('routeChangeStart', onRouteChangeStart);
    };
  }, [onRouteChangeStart]);

It works as I want but I want to add a Custom Confirmation Modal instead of Native Confirmation.

When I added, route changes did not stop. That's why I couldn't wait for the user response.

What can I do? Thank you for your responses.

gungor
  • 114
  • 1
  • 6
  • Looks like others asked this before : https://stackoverflow.com/a/66437866/13749957 – Ramakay Jun 15 '22 at 15:56
  • @Ramakay I guess the difference is that OP doesn't want to use `window.confirm` for the modal, so none of those answers are a solution for OP's question. – juliomalves Jun 20 '22 at 11:03
  • @Ramakay First of all thank you, your send answer doesn't solve my problem as say juliomalves. I don't want to use Confirm Modal. I just want to use a custom modal. – gungor Jun 20 '22 at 12:37

1 Answers1

1

There is a good sample here where it aborts the current route change and saves it to state, prompts the custom model. If confirmed, it pushes the route again.

https://github.com/vercel/next.js/discussions/32231?sort=new?sort=new#discussioncomment-2033546

import { useRouter } from 'next/router';
import React from 'react';
import Dialog from './Dialog';

export interface UnsavedChangesDialogProps {
  shouldConfirmLeave: boolean;
}

export const UnsavedChangesDialog = ({
  shouldConfirmLeave,
}: UnsavedChangesDialogProps): React.ReactElement<UnsavedChangesDialogProps> => {
  const [shouldShowLeaveConfirmDialog, setShouldShowLeaveConfirmDialog] = React.useState(false);
  const [nextRouterPath, setNextRouterPath] = React.useState<string>();

  const Router = useRouter();

  const onRouteChangeStart = React.useCallback(
    (nextPath: string) => {
      if (!shouldConfirmLeave) {
        return;
      }

      setShouldShowLeaveConfirmDialog(true);
      setNextRouterPath(nextPath);

      throw 'cancelRouteChange';
    },
    [shouldConfirmLeave]
  );

  const onRejectRouteChange = () => {
    setNextRouterPath(null);
    setShouldShowLeaveConfirmDialog(false);
  };

  const onConfirmRouteChange = () => {
    setShouldShowLeaveConfirmDialog(false);
    // simply remove the listener here so that it doesn't get triggered when we push the new route.
    // This assumes that the component will be removed anyway as the route changes
    removeListener();
    Router.push(nextRouterPath);
  };

  const removeListener = () => {
    Router.events.off('routeChangeStart', onRouteChangeStart);
  };

  React.useEffect(() => {
    Router.events.on('routeChangeStart', onRouteChangeStart);

    return removeListener;
  }, [onRouteChangeStart]);

  return (
    <Dialog
      title="You have unsaved changes"
      description="Leaving this page will discard unsaved changes. Are you sure?"
      confirmLabel="Discard changes"
      cancelLabel="Go back"
      isOpen={shouldShowLeaveConfirmDialog}
      onConfirm={onConfirmRouteChange}
      onReject={onRejectRouteChange}
    />
  );
};
juliomalves
  • 42,130
  • 20
  • 150
  • 146
SajithK
  • 1,014
  • 12
  • 23
  • I don't know what is working but I am using the above code and it works. Thank you for your answer. – gungor Oct 10 '22 at 21:20