React Router v7中能否用history.listen拦截页面beforeunload事件?
在React Router v7中实现离开页面前保存表单
问题背景
升级到React Router v7后,原通过自定义history实例监听导航并触发beforeunload的方式失效(v7不再支持传入自定义history),而useLocation配合useEffect只能在路由跳转后执行逻辑,无法在跳转前完成表单保存。
解决方案
结合React Router内置钩子与原生事件,同时处理路由跳转和浏览器关闭/刷新两种场景:
1. 封装自定义钩子统一处理
import { useBlocker, useBeforeUnload } from 'react-router-dom'; // 自定义钩子:离开页面(路由跳转/浏览器关闭)前保存表单 function useSaveFormOnLeave(saveFormHandler, isFormModified) { // 拦截路由跳转,跳转前保存表单 useBlocker((navigation) => { if (isFormModified) { saveFormHandler(); // 执行表单保存逻辑 navigation.proceed(); // 允许完成路由跳转 } }); // 监听浏览器关闭/刷新事件,触发保存 useBeforeUnload((event) => { if (isFormModified) { saveFormHandler(); // 触发浏览器默认提示(部分现代浏览器会忽略自定义文本) event.preventDefault(); event.returnValue = ''; } }); }
2. 在表单组件中使用
import { useState, useMemo } from 'react'; function EditForm() { const [formValues, setFormValues] = useState({ name: '', email: '' }); // 假设初始表单值来自接口或props const initialFormValues = { name: '', email: '' }; // 判断表单是否有未保存的修改 const isFormDirty = useMemo(() => { return Object.keys(formValues).some(key => formValues[key] !== initialFormValues[key]); }, [formValues, initialFormValues]); // 表单保存逻辑(示例:可替换为API请求或本地存储) const saveForm = () => { console.log('保存表单数据:', formValues); // 这里可以添加实际保存逻辑,比如: // await api.saveForm(formValues); }; // 使用自定义钩子 useSaveFormOnLeave(saveForm, isFormDirty); // 表单渲染 return ( <form> <input type="text" value={formValues.name} onChange={(e) => setFormValues(prev => ({ ...prev, name: e.target.value }))} placeholder="姓名" /> <input type="email" value={formValues.email} onChange={(e) => setFormValues(prev => ({ ...prev, email: e.target.value }))} placeholder="邮箱" /> </form> ); }
关键说明
useBlocker:React Router v6.4+(含v7)提供的导航拦截钩子,能在路由跳转前触发回调,确保保存逻辑先执行。useBeforeUnload:专门处理浏览器关闭、刷新场景的钩子,与原生beforeunload事件对应。isFormDirty:通过对比表单当前值与初始值,避免无意义的重复保存,优化性能。
内容的提问来源于stack exchange,提问作者user2789261
相关产品推荐
相关产品推荐

