useEffect的清理函数能否阻止React组件卸载?
React导航离开组件时阻止未保存更改的正确实现
为什么useEffect清理函数无法阻止组件卸载
useEffect的清理函数是在组件开始卸载后执行的,此时组件的卸载流程已经启动,返回false也无法中断这个过程,所以这种方式根本无法达到阻止导航离开的目的。
针对路由导航离开的解决方案(React Router v6+)
如果是通过React Router进行页面导航,使用useBlocker钩子可以拦截路由跳转,配合自定义弹窗实现确认逻辑:
import { useBlocker, useNavigate } from 'react-router-dom'; import { useState, useEffect } from 'react'; function EditableComponent() { // 标记是否有未保存更改 const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); // 控制自定义确认弹窗的显示 const [showConfirm, setShowConfirm] = useState(false); const blocker = useBlocker(() => hasUnsavedChanges); const navigate = useNavigate(); // 监听路由拦截状态,触发弹窗 useEffect(() => { if (blocker.state === 'blocked') { setShowConfirm(true); } }, [blocker.state]); // 确认离开:继续路由跳转 const confirmLeave = () => { blocker.proceed(); setShowConfirm(false); }; // 取消离开:重置拦截状态,留在当前组件 const cancelLeave = () => { blocker.reset(); setShowConfirm(false); }; return ( <> <input type="text" onChange={() => setHasUnsavedChanges(true)} placeholder="输入内容..." /> {/* 自定义确认弹窗 */} {showConfirm && ( <div style={{position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', padding: '20px', background: 'white', border: '1px solid #ccc'}}> <p>你有未保存的更改,确定要离开吗?</p> <button onClick={confirmLeave}>确定</button> <button onClick={cancelLeave} style={{marginLeft: '10px'}}>取消</button> </div> )} </> ); }
针对页面刷新/关闭的解决方案
如果需要拦截页面刷新或浏览器关闭的操作,使用beforeunload事件:
import { useState, useEffect } from 'react'; function EditableComponent() { const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); useEffect(() => { const handleBeforeUnload = (e) => { if (hasUnsavedChanges) { // 阻止默认行为,触发浏览器默认确认弹窗 e.preventDefault(); e.returnValue = ''; // 部分浏览器需要设置这个值才能触发弹窗 } }; window.addEventListener('beforeunload', handleBeforeUnload); return () => { window.removeEventListener('beforeunload', handleBeforeUnload); }; }, [hasUnsavedChanges]); return ( <input type="text" onChange={() => setHasUnsavedChanges(true)} placeholder="输入内容..." /> ); }
关键总结
- 路由内导航拦截用
useBlocker,可以完全自定义弹窗逻辑,精准控制是否允许跳转; - 页面刷新/关闭拦截用
beforeunload事件,受浏览器安全限制无法完全自定义弹窗文案,但能有效阻止页面卸载; - 绝对不要依赖useEffect清理函数处理拦截逻辑,时机太晚无法中断卸载流程。
内容的提问来源于stack exchange,提问作者js101
相关产品推荐
相关产品推荐

