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

React页面跳转拦截:自定义弹窗及保存逻辑实现方案咨询

自定义React Router导航拦截弹窗方案(替代Prompt默认弹窗)

嘿,我之前也碰到过这个需求!React Router v6之后确实把setRouteLeaveHook给弃用了,但现在用useBlocker和useNavigate这两个钩子就能完美实现自定义弹窗的需求,比原来的方案还灵活。

下面是具体的实现步骤和代码示例:

1. 核心思路

我们用useBlocker钩子监听导航行为,当需要拦截时(比如表单有未保存内容),触发自定义弹窗;用户点击"确认离开"就放行导航,点击"取消"则留在当前页面。

2. 实现自定义弹窗组件

先写一个自己的弹窗组件,接收确认、取消的回调函数:

import React from 'react';

const CustomLeaveModal = ({ isOpen, onConfirmLeave, onCancel }) => {
  if (!isOpen) return null;

  return (
    <div style={{
      position: 'fixed',
      top: 0,
      left: 0,
      right: 0,
      bottom: 0,
      background: 'rgba(0,0,0,0.5)',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center'
    }}>
      <div style={{
        background: 'white',
        padding: '2rem',
        borderRadius: '8px',
        boxShadow: '0 2px 10px rgba(0,0,0,0.2)'
      }}>
        <h3>你有未保存的更改</h3>
        <p>离开页面会丢失当前编辑内容,确定要离开吗?</p>
        <div style={{ marginTop: '1rem', display: 'flex', gap: '1rem' }}>
          <button onClick={onConfirmLeave} style={{ padding: '0.5rem 1rem', background: '#dc3545', color: 'white', border: 'none', borderRadius: '4px' }}>
            确认离开
          </button>
          <button onClick={onCancel} style={{ padding: '0.5rem 1rem', background: '#6c757d', color: 'white', border: 'none', borderRadius: '4px' }}>
            取消
          </button>
        </div>
      </div>
    </div>
  );
};

export default CustomLeaveModal;

3. 在目标页面中使用拦截逻辑

在需要拦截导航的页面组件里,引入钩子和弹窗组件,处理拦截逻辑:

import React, { useState } from 'react';
import { useBlocker, useNavigate } from 'react-router-dom';
import CustomLeaveModal from './CustomLeaveModal';

const EditPage = () => {
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
  const [showLeaveModal, setShowLeaveModal] = useState(false);
  const navigate = useNavigate();
  const blocker = useBlocker((tx) => {
    // 只有当有未保存更改时才拦截
    if (hasUnsavedChanges) {
      setShowLeaveModal(true);
      // 先暂停导航
      tx.preventDefault();
    } else {
      // 允许导航
      tx.retry();
    }
  });

  // 确认离开的处理函数,这里可以先执行保存操作再放行
  const handleConfirmLeave = () => {
    // 这里可以添加你的自定义保存逻辑,比如调用API保存表单
    console.log('执行保存操作...');
    setShowLeaveModal(false);
    // 放行之前被拦截的导航
    blocker.proceed();
  };

  const handleCancel = () => {
    setShowLeaveModal(false);
    // 取消导航,留在当前页面
    blocker.reset();
  };

  // 模拟表单编辑,触发未保存状态
  const handleInputChange = () => {
    setHasUnsavedChanges(true);
  };

  return (
    <div>
      <h1>编辑页面</h1>
      <input type="text" onChange={handleInputChange} placeholder="输入内容..." />
      <CustomLeaveModal 
        isOpen={showLeaveModal}
        onConfirmLeave={handleConfirmLeave}
        onCancel={handleCancel}
      />
    </div>
  );
};

export default EditPage;

关键说明

  • useBlocker的回调函数会在导航发生时触发,参数tx是导航事务对象,tx.preventDefault()可以暂停导航,tx.retry()可以重试导航。
  • blocker.proceed()用来放行之前被拦截的导航,blocker.reset()用来取消导航,让用户留在当前页面。
  • 你可以在handleConfirmLeave里添加任何自定义逻辑,比如先调用保存接口,成功后再放行导航,完全满足你的需求。

这个方案完全替代了旧的setRouteLeaveHook,而且适配React Router v6及以上版本,自定义弹窗的样式和逻辑都可以完全由你控制,比默认的Prompt弹窗灵活多了!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:32:49