Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Next JS: Warn User for Unsaved Form before Route Change

Tags:

next.js

In Next How can i stop Router Navigation in Next JS.

I am trying to use routerChangeStart event to stop navigation.

useEffect(() => {
    const handleRouteChange = (url: string): boolean => {
      if (dirty) { 
        return false;
      }
      return true;
    };

    Router.events.on('routeChangeStart', handleRouteChange);
    return () => {
      Router.events.off('routeChangeStart', handleRouteChange);
    };
  }, []);
like image 953
Muhammad Awais Avatar asked Jul 23 '20 23:07

Muhammad Awais


3 Answers

It seems there is no perfect way to this but I handle it with this little trick:

React.useEffect(() => {
  const confirmationMessage = 'Changes you made may not be saved.';
  const beforeUnloadHandler = (e: BeforeUnloadEvent) => {
    (e || window.event).returnValue = confirmationMessage;
    return confirmationMessage; // Gecko + Webkit, Safari, Chrome etc.
  };
  const beforeRouteHandler = (url: string) => {
    if (Router.pathname !== url && !confirm(confirmationMessage)) {
      // to inform NProgress or something ...
      Router.events.emit('routeChangeError');
      // tslint:disable-next-line: no-string-throw
      throw `Route change to "${url}" was aborted (this error can be safely ignored). See https://github.com/zeit/next.js/issues/2476.`;
    }
  };
  if (notSaved) {
    window.addEventListener('beforeunload', beforeUnloadHandler);
    Router.events.on('routeChangeStart', beforeRouteHandler);
  } else {
    window.removeEventListener('beforeunload', beforeUnloadHandler);
    Router.events.off('routeChangeStart', beforeRouteHandler);
  }
  return () => {
    window.removeEventListener('beforeunload', beforeUnloadHandler);
    Router.events.off('routeChangeStart', beforeRouteHandler);
  };
}, [notSaved]);

This code will interrupt changing route (with nextJs Route and also browser refresh / close tab action)

like image 144
Alireza Esfahani Avatar answered Nov 19 '22 06:11

Alireza Esfahani


You can write a custom hook.

import Router from 'next/router';
import { useEffect } from 'react';

const useWarnIfUnsavedChanges = (unsavedChanges, callback) => {
    useEffect(() => {
      const routeChangeStart = url => {
        if (unsavedChanges) {
          Router.events.emit('routeChangeError');
          Router.replace(Router, Router.asPath, { shallow: true });
          throw 'Abort route change. Please ignore this error.';
        }
      };

      Router.events.on('routeChangeStart', routeChangeStart);

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

export default useWarnIfUnsavedChanges;

Take inspiration from: https://github.com/vercel/next.js/discussions/12348#discussioncomment-8089

like image 3
Do Anh Bon Avatar answered Nov 19 '22 06:11

Do Anh Bon


Here's my custom hook solution that seems to cut it, written in TypeScript.

import Router from "next/router"
import { useEffect } from "react"

const useWarnIfUnsavedChanges = (unsavedChanges: boolean, callback: () => boolean) => {
  useEffect(() => {
    if (unsavedChanges) {
      const routeChangeStart = () => {
        const ok = callback()
        if (!ok) {
          Router.events.emit("routeChangeError")
          throw "Abort route change. Please ignore this error."
        }
      }
      Router.events.on("routeChangeStart", routeChangeStart)

      return () => {
        Router.events.off("routeChangeStart", routeChangeStart)
      }
    }
  }, [unsavedChanges])
}

You can use it in your component as follows:

useWarnIfUnsavedChanges(changed, () => {
  return confirm("Warning! You have unsaved changes.")
})
like image 3
raimohanska Avatar answered Nov 19 '22 07:11

raimohanska