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

React Router v6+Antd Form中阻止页面返回/刷新并弹出未保存提示

问题描述

使用Antd Form开发表单页面,需求是用户刷新页面或点击浏览器返回按钮时,若表单存在未保存修改则弹出提示。目前刷新页面的提示功能正常,但点击浏览器返回按钮无任何响应,尝试多种基于history的实现方式均无效,使用的是React Router v6。

原因分析
  1. 自行创建的createBrowserHistory()实例和React Router v6内部维护的history实例不是同一个,因此调用该实例的block方法无法监听React Router的路由变化(包括返回按钮触发的POP动作)。
  2. React Router v6移除了useHistory的block方法,原有的history.block用法不再适用。
解决方案

方案一:使用React Router v6的unstable_block(临时方案,可等待稳定API)

React Router v6提供了不稳定的unstable_block方法,可通过useNavigation钩子获取:

import React, { useEffect } from 'react';
import { Form, Input, Button } from 'antd';
import { useNavigate, useNavigation } from 'react-router-dom';

const ExampleForm = () => {
  const [form] = Form.useForm();
  const navigate = useNavigate();
  const navigation = useNavigation();

  useEffect(() => {
    // 监听路由变化,拦截未保存的修改
    const unblock = navigation.unstable_block((tx) => {
      if (form.isFieldsTouched(true)) { // true表示忽略初始值,只要有修改就算
        const confirmLeave = window.confirm('你有未保存的修改,确定要离开吗?');
        if (confirmLeave) {
          unblock(); // 解除拦截,允许跳转
          tx.retry(); // 重试路由跳转
        }
        return false; // 拦截当前跳转
      }
      return true; // 允许跳转
    });

    return () => unblock(); // 组件卸载时解除拦截
  }, [form, navigation]);

  const handleSubmit = () => {
    form.validateFields().then(values => {
      // 处理表单提交逻辑
      console.log(values);
      form.resetFields();
      navigate('/');
    });
  };

  return (
    <Form form={form} onFinish={handleSubmit}>
      <Form.Item label="Name" name="name" rules={[{ required: true }]}>
        <Input />
      </Form.Item>
      <Form.Item label="Email" name="email" rules={[{ required: true, type: 'email' }]}>
        <Input />
      </Form.Item>
      <Form.Item>
        <Button type="primary" htmlType="submit">
          Submit
        </Button>
      </Form.Item>
    </Form>
  );
};

export default ExampleForm;

方案二:监听popstate事件(稳定方案)

通过监听浏览器的popstate事件处理返回按钮动作,结合Antd Form的状态判断:

import React, { useEffect, useState } from 'react';
import { Form, Input, Button, Modal } from 'antd';
import { useNavigate, useLocation } from 'react-router-dom';

const ExampleForm = () => {
  const [form] = Form.useForm();
  const navigate = useNavigate();
  const location = useLocation();
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [lastLocation, setLastLocation] = useState(location);
  const [isBlocking, setIsBlocking] = useState(false);

  // 处理刷新页面的提示
  useEffect(() => {
    const handleBeforeUnload = (e) => {
      if (form.isFieldsTouched(true)) {
        e.preventDefault();
        e.returnValue = '';
      }
    };
    window.addEventListener('beforeunload', handleBeforeUnload);
    return () => window.removeEventListener('beforeunload', handleBeforeUnload);
  }, [form]);

  // 监听popstate事件(返回按钮触发)
  useEffect(() => {
    const handlePopState = () => {
      if (form.isFieldsTouched(true) && !isBlocking) {
        setIsModalVisible(true);
        // 回退到当前页面,阻止默认返回动作
        navigate(lastLocation.pathname);
      }
    };
    window.addEventListener('popstate', handlePopState);
    return () => window.removeEventListener('popstate', handlePopState);
  }, [form, navigate, lastLocation, isBlocking]);

  // 记录当前路由,用于拦截后恢复
  useEffect(() => {
    if (!isModalVisible) {
      setLastLocation(location);
    }
  }, [location, isModalVisible]);

  // 确认离开的逻辑
  const handleConfirmLeave = () => {
    setIsBlocking(false);
    setIsModalVisible(false);
    // 执行返回动作
    navigate(-1);
  };

  // 取消离开,留在当前页面
  const handleCancelLeave = () => {
    setIsBlocking(false);
    setIsModalVisible(false);
  };

  const handleSubmit = () => {
    form.validateFields().then(values => {
      console.log(values);
      form.resetFields();
      navigate('/');
    });
  };

  return (
    <>
      <Form form={form} onFinish={handleSubmit}>
        <Form.Item label="Name" name="name" rules={[{ required: true }]}>
          <Input />
        </Form.Item>
        <Form.Item label="Email" name="email" rules={[{ required: true, type: 'email' }]}>
          <Input />
        </Form.Item>
        <Form.Item>
          <Button type="primary" htmlType="submit">
            Submit
          </Button>
        </Form.Item>
      </Form>

      <Modal
        title="提示"
        visible={isModalVisible}
        onOk={handleConfirmLeave}
        onCancel={handleCancelLeave}
        okText="确定离开"
        cancelText="留在当前页面"
      >
        <p>你有未保存的修改,确定要离开吗?</p>
      </Modal>
    </>
  );
};

export default ExampleForm;

关键注意点

  • 不要自行创建createBrowserHistory()实例,必须使用React Router提供的导航钩子(useNavigate、useNavigation),确保和路由内部实例一致。
  • 使用Antd Form的form.isFieldsTouched(true)判断表单是否有修改,true参数会忽略初始值,只要用户修改过字段就会返回true。
  • 方案二中需要手动处理路由回退和状态恢复,避免出现路由跳转异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 09:45:42