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

React 18类组件:文件上传时阻止用户离开页面的实现方案

解决React类组件中文件上传时页面刷新/返回的确认弹窗问题

问题背景

React 18类组件中,文件上传过程中需要拦截两种操作:页面刷新、浏览器返回按钮点击,弹出确认弹窗提示用户。此前尝试在componentDidMount中添加beforeunload事件监听器,存在两个问题:返回按钮操作未触发弹窗,页面刷新的弹窗触发也不稳定。

解决方案

需要分别处理页面刷新/关闭和浏览器返回两种场景,以下是具体实现步骤:

1. 修复beforeunload事件监听(处理刷新/页面关闭)

beforeunload的触发逻辑需要严格遵循浏览器规范,必须正确调用事件对象的preventDefault()并设置returnValue,同时要在组件卸载时移除监听避免内存泄漏。

2. 添加popstate事件监听(处理浏览器返回)

beforeunload无法拦截单页应用的路由返回操作,需要通过监听popstate事件实现,同时要区分正常路由跳转和用户主动返回的场景。

完整修改后的组件代码

class FileUploadComponent extends React.Component<Props, State> {
  state = {
    uploadingFile: false,
  };

  static contextType = ConfirmationContext;
  // 存储初始路由栈长度,用于区分正常跳转和返回操作
  initialHistoryLength: number;

  componentDidMount = async () => {
    this.checkForStagedUpdates();
    // 初始化路由栈长度
    this.initialHistoryLength = window.history.length;
    // 添加beforeunload监听
    window.addEventListener('beforeunload', this.handleBeforeUnload);
    // 添加popstate监听
    window.addEventListener('popstate', this.handlePopState);
  };

  componentWillUnmount = () => {
    // 移除事件监听,避免内存泄漏
    window.removeEventListener('beforeunload', this.handleBeforeUnload);
    window.removeEventListener('popstate', this.handlePopState);
  };

  // 处理页面刷新/关闭
  handleBeforeUnload = (e: BeforeUnloadEvent) => {
    if (this.state.uploadingFile) {
      // 遵循浏览器规范,必须调用preventDefault
      e.preventDefault();
      // 设置返回值,部分浏览器需要这个来显示弹窗
      e.returnValue = '文件正在上传中,确定要离开此页面吗?';
      // 返回提示文本(部分旧浏览器需要)
      return '文件正在上传中,确定要离开此页面吗?';
    }
  };

  // 处理浏览器返回按钮
  handlePopState = (e: PopStateEvent) => {
    if (this.state.uploadingFile) {
      // 阻止默认返回行为
      e.preventDefault();
      // 弹出确认框
      const confirmLeave = window.confirm('文件正在上传中,确定要返回吗?');
      if (confirmLeave) {
        // 用户确认返回,再次执行返回操作(需要先移除监听避免循环触发)
        window.removeEventListener('popstate', this.handlePopState);
        window.history.back();
        // 重新添加监听
        setTimeout(() => {
          window.addEventListener('popstate', this.handlePopState);
        }, 100);
      } else {
        // 用户取消返回,把路由状态恢复回去
        window.history.pushState(null, '', window.location.href);
      }
    }
  };

  checkForStagedUpdates = async () => {
    // 原有逻辑保持不变
    .
    .
    .
  };

  uploadFile = async (file: File) => {
    this.setState({
      uploadingFile: true,
    });

    try {
      await uploadUpdateFile(file);
      this.props.notify({
        type: 'success',
        body: 'File uploaded',
      });
      this.checkForStagedUpdates();
    } catch (err) {
      console.log(err);
      if (err.message.includes('405')) {
        this.props.notify({ type: 'error', body: 'Invalid file' });
      } else {
        this.props.notify({
          type: 'error',
          body: 'There was a problem uploading the file',
        });
      }
    } finally {
      this.setState({ uploadingFile: false });
    }
  };
.
.
.
}

注意事项

  • beforeunload的提示文本内容在部分现代浏览器中会被替换为浏览器默认文本,无法自定义,但不影响弹窗触发。
  • popstate事件监听需要注意路由状态的恢复逻辑,避免出现路由异常。
  • 必须在组件卸载时移除所有事件监听,防止内存泄漏和其他页面的异常触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 08:03:26