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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 16:46:15