UnsavedAlertDialog.tsx 2.3 KB

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