【问题标题】:Next JS: Warn User for Unsaved Form before Route ChangeNext JS:在路由更改之前警告用户未保存的表单
【发布时间】:2021-11-05 23:05:47
【问题描述】:

在 Next 如何在 Next JS 中停止路由器导航。

我正在尝试使用routerChangeStart 事件来停止导航。

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

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

【问题讨论】:

标签: next.js


【解决方案1】:

似乎没有完美的方法,但我用这个小技巧来处理它:

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]);

此代码将中断更改路线(使用 nextJs 路线以及浏览器刷新/关闭选项卡操作)

【讨论】:

  • 似乎第 4 行 ((e || window.event).returnValue = confirmationMessage;) 也是检测页面刷新所必需的,至少在 Chrome 上是这样。
  • 在 Next 12 中,当您第一次点击返回按钮时,您的 beforeRouteHandler 将被调用。如果您取消然后再次点击后退按钮,则由于某种原因您的beforeUnloadHandler 会被调用。如果您接受,您最终将返回浏览器历史记录两次。
  • 我最终通过使用router.beforePopState添加第三种类型的检查来处理它
【解决方案2】:

您可以编写自定义挂钩。

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;

灵感来自:https://github.com/vercel/next.js/discussions/12348#discussioncomment-8089

【讨论】:

  • 我们如何使用它?
  • useWarnIfUnsavedChanges(unsavedChange, showNotification) <Notification onOk={() => updateUnsavedChange(false)} />
  • 我试过这个,但它似乎有点错误。回调参数实际上并没有使用,我认为最好仅在有 unsavedChanges 时才分配 routeChangeStart 侦听器。将我的版本作为单独的答案发布。谢谢你的灵感!
【解决方案3】:

这是我用 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])
}

你可以在你的组件中使用它,如下所示:

useWarnIfUnsavedChanges(changed, () => {
  return confirm("Warning! You have unsaved changes.")
})

【讨论】:

    猜你喜欢
    • 2016-06-25
    • 2021-03-02
    • 1970-01-01
    • 2019-03-21
    • 2019-06-18
    • 1970-01-01
    • 2017-12-30
    • 2018-11-28
    • 1970-01-01
    相关资源
    最近更新 更多