Next.js:更改路线前警告用户未保存表单

vpfxa7rd  于 2023-01-13  发布在  其他
关注(0)|答案(5)|浏览(149)

在下一个我怎么能停止路由器导航在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);
    };
  }, []);
zysjyyx4

zysjyyx41#

感谢@raimohanska提供的好的解决方案。我做了一个小的更新,包括确认页面重新加载以及:

* Asks for confirmation to leave/reload if there are unsaved changes.
 */
import Router from 'next/router';
import { useEffect } from 'react';

export const useOnLeavePageConfirmation = (unsavedChanges: boolean) => {
  useEffect(() => {
    // For reloading.
    window.onbeforeunload = () => {
      if (unsavedChanges) {
        return 'You have unsaved changes. Do you really want to leave?';
      }
    };

    // For changing in-app route.
    if (unsavedChanges) {
      const routeChangeStart = () => {
        const ok = confirm('You have unsaved changes. Do you really want to leave?');
        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);
      };
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [unsavedChanges]);
};

用法:

useOnLeavePageConfirmation(changesUnsaved);
o2gm4chl

o2gm4chl2#

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

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 Route和浏览器刷新/关闭选项卡操作)

brjng4g3

brjng4g33#

您可以编写自定义钩子。

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

3htmauhk

3htmauhk4#

下面是我的自定义钩子解决方案,它似乎可以解决这个问题,用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.")
})
jmo0nnb3

jmo0nnb35#

你需要做一个钩子来阻止路由器的改变。但是为了让它正常工作,你应该知道你的表单是否是原始的。为了用react-final-form做到这一点,他们有一个FormSpy组件可以订阅:

import { Form, FormSpy } from 'react-final-form'
import { useWarnIfUnsaved } from '@hooks/useWarnIfUnsaved'

const [isPristine, setPristine] = useState(true)
useWarnIfUnsaved(!isPristine, () => {
  return confirm('Warning! You have unsaved changes.')
})
return (
  <Form
    render={({ handleSubmit, submitting, submitError }) => {
      return (
        <>
          <FormSpy subscription={{ pristine: true }}>
            {(props) => {
              setPristine(props.pristine)
              return undefined
            }}
          </FormSpy>
...

@raimohanska建议的Typescript钩子对我很有效:

import Router from "next/router"
import { useEffect } from "react"

export const useWarnIfUnsaved = (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])
}

相关问题