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
相关产品推荐
相关产品推荐

