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

React-Router-Dom v6实现编辑页跳转前未保存更改提示

解决方案:React Router v6 下路由跳转时的未保存更改确认

你之前的beforeunload监听仅对浏览器刷新/关闭标签页有效,但React Router的单页内部路由跳转不会触发这个事件。下面提供两种适配React Router v6的实现方案:


方案一:使用官方不稳定API unstable_useBlocker(推荐)

这个API能全局拦截所有路由跳转行为,覆盖浏览器前进后退、导航链接点击等所有场景,虽然标记为"不稳定",但实际项目中已被广泛使用,后续大概率会转为稳定API。

完整代码示例

import { useState, useEffect } from 'react';
import { unstable_useBlocker as useBlocker, useNavigate, Link } from 'react-router-dom';

const PersonEditPage = () => {
  const navigate = useNavigate();
  // 模拟从接口获取的原始表单数据
  const [originalFormData, setOriginalFormData] = useState({ name: '张三', age: 25 });
  const [formData, setFormData] = useState(originalFormData);
  
  // 判断表单是否有未保存更改
  const isDirty = JSON.stringify(formData) !== JSON.stringify(originalFormData);

  // 拦截路由跳转:当有未保存更改且跳转到其他路由时阻塞
  const blocker = useBlocker(({ currentLocation, nextLocation }) => {
    return isDirty && currentLocation.pathname !== nextLocation.pathname;
  });

  // 处理阻塞后的确认逻辑
  useEffect(() => {
    if (blocker.state === 'blocked') {
      const shouldLeave = window.confirm('是否离开页面?你已做出更改,离开将丢失未保存内容。');
      shouldLeave ? blocker.proceed() : blocker.reset();
    }
  }, [blocker]);

  // 表单保存逻辑
  const handleSave = () => {
    // 这里替换为实际的接口保存逻辑
    setOriginalFormData(formData);
    alert('保存成功');
  };

  return (
    <div>
      {/* 左上角Logo用Link组件,路由跳转将被自动拦截 */}
      <Link to="/home">网站Logo</Link>
      
      <div className="edit-form">
        <h2>编辑人员信息</h2>
        <input
          type="text"
          value={formData.name}
          onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
          placeholder="姓名"
        />
        <input
          type="number"
          value={formData.age}
          onChange={(e) => setFormData(prev => ({ ...prev, age: Number(e.target.value) }))}
          placeholder="年龄"
        />
        <button onClick={handleSave}>保存更改</button>
      </div>
    </div>
  );
};

export default PersonEditPage;

关键说明

  • useBlocker接收一个回调函数,返回true则阻塞当前路由跳转
  • 当路由被阻塞时,blocker.state变为blocked,此时弹出系统确认框,根据用户选择调用blocker.proceed()(允许跳转)或blocker.reset()(取消跳转)
  • 保留你原有的beforeunload监听代码,可以同时覆盖浏览器刷新/关闭的场景

方案二:手动处理特定跳转入口(简单场景)

如果不想使用不稳定API,可以直接在Logo的点击事件中添加判断逻辑,仅处理该跳转场景:

代码示例

import { useState, useNavigate } from 'react';

const PersonEditPage = () => {
  const navigate = useNavigate();
  const [originalName, setOriginalName] = useState('张三');
  const [name, setName] = useState(originalName);
  const isDirty = name !== originalName;

  // 处理Logo点击事件
  const handleLogoClick = () => {
    if (isDirty) {
      const shouldLeave = window.confirm('是否离开页面?你已做出更改,离开将丢失未保存内容。');
      if (shouldLeave) {
        navigate('/home');
      }
    } else {
      navigate('/home');
    }
  };

  return (
    <div>
      {/* 将Logo改为可点击元素,绑定自定义点击事件 */}
      <div onClick={handleLogoClick} style={{ cursor: 'pointer' }}>网站Logo</div>
      
      {/* 表单内容... */}
    </div>
  );
};

注意事项

这种方式仅处理Logo点击的跳转,无法拦截浏览器前进后退、其他导航链接的跳转,适合跳转入口较少的简单场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 18:20:39