Next JS:在路由更改之前警告用户未保存的表单

Muh*_*ais 5 next.js

在 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);
    };
  }, []);
Run Code Online (Sandbox Code Playgroud)

rai*_*ska 19

这是我的自定义钩子解决方案,它是用 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])
}
Run Code Online (Sandbox Code Playgroud)

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

useWarnIfUnsavedChanges(changed, () => {
  return confirm("Warning! You have unsaved changes.")
})
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案!值得更多关注! (3认同)
  • 感谢分享。请注意,他们在 v13+ 中删除了这些路由器事件,而且还没有替代方案,所以在那里不起作用。我还没有升级的原因之一。https://github.com/vercel/next.js/discussions/42016 (2认同)

小智 7

您可以编写自定义挂钩。

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;
Run Code Online (Sandbox Code Playgroud)

从以下地方获取灵感:https ://github.com/vercel/next.js/discussions/12348#discussioncomment-8089

  • 我尝试过这个,但似乎有点问题。回调参数实际上并未使用,我认为最好仅在存在未保存的更改时分配routeChangeStart侦听器。将我的版本作为单独的答案发布。谢谢你的灵感! (3认同)

小智 5

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

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

Run Code Online (Sandbox Code Playgroud)

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

  • 在 Next 12 中,第一次点击后退按钮时,将调用“beforeRouteHandler”。如果您取消然后再次点击后退按钮,由于某种原因,您的“beforeUnloadHandler”会在此时被调用。如果您接受,您最终将在浏览器历史记录中返回两次。 (2认同)