You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中表单未保存时阻止页面刷新与路由跳转

处理未保存表单状态下的React路由跳转拦截(React Router v6.4.4)

问题背景

开发React应用时,需要在表单内容处于未保存(dirty)状态时阻止用户跳转到其他页面。现有代码仅能拦截页面刷新/关闭操作,但无法阻止React Router的单页路由跳转,且由于使用React Router v6.4.4,无法使用Prompt、useHistory或useBlocking等废弃/移除的API。

解决方案

利用React Router v6.4+提供的useNavigation钩子,结合原有的beforeunload监听,同时实现页面刷新和路由跳转的双重拦截。

修改后的自定义Hook代码

import { useEffect, useState } from "react";
import { useNavigation } from "react-router-dom";

const useUnsavedChanges = () => {
  const [isDirty, setDirty] = useState(false);
  const navigation = useNavigation();

  useEffect(() => {
    // 拦截页面刷新/关闭
    function confirmPageUnload(event) {
      if (isDirty) {
        event.preventDefault();
        event.returnValue = "Are you sure you want to leave? Your changes will be lost.";
      }
    }

    window.addEventListener("beforeunload", confirmPageUnload);

    // 拦截React路由跳转
    let unblock;
    if (isDirty) {
      unblock = navigation.block((tx) => {
        const confirmLeave = window.confirm("Are you sure you want to leave? Your changes will be lost.");
        if (confirmLeave) {
          unblock();
          tx.retry();
        }
      });
    }

    // 清理副作用
    return () => {
      window.removeEventListener("beforeunload", confirmPageUnload);
      if (unblock) unblock();
    };
  }, [isDirty, navigation]);

  const onDirty = () => setDirty(true);
  const onPristine = () => setDirty(false);

  return { onDirty, onPristine, isDirty };
};

export default useUnsavedChanges;

代码说明

  • useNavigation钩子:获取React Router的导航实例,通过其block方法拦截路由跳转动作。
  • 路由拦截逻辑:当表单处于dirty状态时,调用navigation.block()注册拦截回调。用户触发路由跳转时,会弹出确认框:
    • 若用户确认离开,调用unblock()取消拦截,再通过tx.retry()执行原导航动作;
    • 若用户取消,则导航被阻止。
  • 副作用清理:组件卸载或isDirty状态变化时,移除beforeunload监听并取消路由拦截,避免内存泄漏。

组件使用示例

在表单组件中引入并使用该Hook,在表单内容变化时标记为dirty,保存/重置后恢复为pristine:

import { useUnsavedChanges } from './useUnsavedChanges';

function MyForm() {
  const { onDirty, onPristine } = useUnsavedChanges();

  const handleInputChange = () => {
    onDirty();
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    // 执行表单提交逻辑
    // await submitForm();
    onPristine();
  };

  const handleReset = () => {
    // 执行表单重置逻辑
    onPristine();
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" onChange={handleInputChange} placeholder="输入内容..." />
      <button type="submit">保存</button>
      <button type="button" onClick={handleReset}>重置</button>
    </form>
  );
}

注意事项

  • 确保使用该Hook的组件处于RouterProvider的上下文范围内(React Router v6.4+推荐的路由配置方式);
  • 该方案能拦截所有React Router发起的导航,包括Link点击、navigate函数调用、Form提交等场景。

内容的提问来源于stack exchange,提问作者Ghayas Ud Din

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 10:05:28