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

React中拦截路由跳转:Router v6.26.0下未保存表单的跳转处理

在React Router v6.26.0中拦截未保存表单的路由跳转

要实现表单未保存时拦截路由跳转并显示自定义确认模态框,你可以利用React Router v6.4+提供的导航阻塞API(useBlocker和useNavigation钩子),结合自定义状态管理来实现。以下是具体步骤和代码示例:

核心实现思路

  1. 追踪表单的「脏状态」:判断表单内容是否有未保存的修改。
  2. 使用useBlocker监听导航行为,当表单脏且不是提交操作时阻塞导航。
  3. 导航被阻塞时显示自定义模态框,根据用户选择决定继续或取消导航。

步骤1:创建自定义钩子管理表单未保存状态

这个钩子封装了导航阻塞逻辑和模态框控制:

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

export function useFormUnsavedChanges(isDirty) {
  // 定义阻塞规则:表单脏时阻塞导航,排除提交触发的导航
  const blocker = useBlocker((tx) => isDirty && !tx.state?.isSubmitting);
  
  const [showConfirmModal, setShowConfirmModal] = useState(false);
  const [pendingNavigation, setPendingNavigation] = useState(null);

  // 监听导航阻塞事件,弹出模态框
  useEffect(() => {
    if (blocker.state === 'blocked') {
      setPendingNavigation(blocker.navigation);
      setShowConfirmModal(true);
    }
  }, [blocker]);

  // 用户确认离开:继续导航
  const handleConfirmLeave = async () => {
    setShowConfirmModal(false);
    await pendingNavigation?.proceed();
    setPendingNavigation(null);
  };

  // 用户取消离开:终止导航
  const handleCancelLeave = () => {
    setShowConfirmModal(false);
    pendingNavigation?.cancel();
    setPendingNavigation(null);
  };

  // 表单保存后自动取消阻塞
  useEffect(() => {
    if (!isDirty && pendingNavigation) {
      handleCancelLeave();
    }
  }, [isDirty]);

  return { showConfirmModal, handleConfirmLeave, handleCancelLeave };
}

步骤2:在表单页面中使用钩子

以/profile页面为例,集成钩子并渲染自定义模态框:

import { useState } from 'react';
import { useFormUnsavedChanges } from './useFormUnsavedChanges';

function ProfilePage() {
  const [formData, setFormData] = useState({ name: '', email: '' });
  const initialFormData = { name: '', email: '' };
  
  // 判断表单是否有未保存修改
  const isDirty = JSON.stringify(formData) !== JSON.stringify(initialFormData);

  // 调用自定义钩子
  const { showConfirmModal, handleConfirmLeave, handleCancelLeave } = useFormUnsavedChanges(isDirty);

  // 模拟表单保存逻辑
  const handleSave = () => {
    console.log('保存表单:', formData);
    setFormData(initialFormData); // 保存后重置脏状态
  };

  return (
    <div>
      <h1>个人资料</h1>
      <input
        type="text"
        value={formData.name}
        onChange={(e) => setFormData({ ...formData, name: e.target.value })}
        placeholder="姓名"
      />
      <input
        type="email"
        value={formData.email}
        onChange={(e) => setFormData({ ...formData, email: e.target.value })}
        placeholder="邮箱"
      />
      <button onClick={handleSave}>保存修改</button>

      {/* 自定义确认模态框 */}
      {showConfirmModal && (
        <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.5)', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
          <div style={{ background: 'white', padding: '2rem', borderRadius: '8px' }}>
            <h3>提示</h3>
            <p>您有未保存的更改,确定要离开当前页面吗?</p>
            <div style={{ marginTop: '1rem', display: 'flex', gap: '1rem' }}>
              <button onClick={handleConfirmLeave}>确定离开</button>
              <button onClick={handleCancelLeave}>继续编辑</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

export default ProfilePage;

步骤3:确保路由环境支持

React Router的导航阻塞API需要使用数据路由(即通过createBrowserRouter创建路由,并使用RouterProvider渲染),示例路由配置:

import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import ProfilePage from './ProfilePage';
import AboutPage from './AboutPage';

const router = createBrowserRouter([
  { path: '/profile', element: <ProfilePage /> },
  { path: '/about', element: <AboutPage /> },
]);

function App() {
  return <RouterProvider router={router} />;
}

export default App;

关键说明

  • useBlocker的回调返回true时会触发导航阻塞,你可以根据需求调整阻塞条件(比如排除特定路由跳转)。
  • blocker.navigation对象提供的proceed()和cancel()方法用来控制导航的继续或终止。
  • 模态框的样式可以根据你的项目需求自定义,这里使用内联样式仅作示例。

内容的提问来源于stack exchange,提问作者Dex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 15:03:17