UnsavedAlertDialog.tsx 2.2 KB

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