UnsavedAlertDialog.tsx 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import React, { type JSX, memo, useCallback, useEffect } from 'react';
  2. import { useRouter } from 'next/router';
  3. import { useTranslation } from 'next-i18next';
  4. import { useUnsavedWarning } from '~/states/ui/unsaved-warning';
  5. const UnsavedAlertDialog = (): JSX.Element => {
  6. const { t } = useTranslation();
  7. const router = useRouter();
  8. const { isEnabled: isEnabledUnsavedWarning, reset } = useUnsavedWarning();
  9. const alertUnsavedWarningByBrowser = useCallback(
  10. (e) => {
  11. if (isEnabledUnsavedWarning) {
  12. e.preventDefault();
  13. // returnValue should be set to show alert dialog
  14. // default alert message cannot be changed.
  15. // See -> https://developer.mozilla.org/ja/docs/Web/API/Window/beforeunload_event
  16. e.returnValue = '';
  17. return;
  18. }
  19. },
  20. [isEnabledUnsavedWarning],
  21. );
  22. const alertUnsavedWarningByNextRouter = useCallback(() => {
  23. if (isEnabledUnsavedWarning) {
  24. // see: https://zenn.dev/qaynam/articles/c4794537a163d2
  25. // eslint-disable-next-line no-alert
  26. const answer = window.confirm(t('page_edit.changes_not_saved'));
  27. if (!answer) {
  28. // eslint-disable-next-line no-throw-literal
  29. throw 'Abort route';
  30. }
  31. }
  32. }, [isEnabledUnsavedWarning, t]);
  33. const onRouterChangeComplete = useCallback(() => {
  34. reset();
  35. }, [reset]);
  36. /*
  37. * Route changes by Browser
  38. * Example: window.location.href, F5
  39. */
  40. useEffect(() => {
  41. window.addEventListener('beforeunload', alertUnsavedWarningByBrowser);
  42. return () => {
  43. window.removeEventListener('beforeunload', alertUnsavedWarningByBrowser);
  44. };
  45. }, [alertUnsavedWarningByBrowser]);
  46. /*
  47. * Route changes by Next Router
  48. * https://nextjs.org/docs/api-reference/next/router
  49. */
  50. useEffect(() => {
  51. router.events.on('routeChangeStart', alertUnsavedWarningByNextRouter);
  52. return () => {
  53. router.events.off('routeChangeStart', alertUnsavedWarningByNextRouter);
  54. };
  55. }, [alertUnsavedWarningByNextRouter, router.events]);
  56. useEffect(() => {
  57. router.events.on('routeChangeComplete', onRouterChangeComplete);
  58. return () => {
  59. router.events.off('routeChangeComplete', onRouterChangeComplete);
  60. };
  61. }, [onRouterChangeComplete, router.events]);
  62. return <></>;
  63. };
  64. export default memo(UnsavedAlertDialog);