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

