React中拦截路由跳转:Router v6.26.0下未保存表单的跳转处理
在React Router v6.26.0中拦截未保存表单的路由跳转
要实现表单未保存时拦截路由跳转并显示自定义确认模态框,你可以利用React Router v6.4+提供的导航阻塞API(useBlocker和useNavigation钩子),结合自定义状态管理来实现。以下是具体步骤和代码示例:
核心实现思路
- 追踪表单的「脏状态」:判断表单内容是否有未保存的修改。
- 使用
useBlocker监听导航行为,当表单脏且不是提交操作时阻塞导航。 - 导航被阻塞时显示自定义模态框,根据用户选择决定继续或取消导航。
步骤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
相关产品推荐
相关产品推荐

