如何在React Router v3中阻止用户意外路由切换?
解决React Router v3中阻止意外路由切换及页面刷新的方案
嘿,我完全懂你现在的困扰——React Router v3里没有v4的<Prompt>组件,setRouteLeaveHook又管不了页面刷新这类浏览器层面的操作,别担心,我们可以把两个方法结合起来,完美覆盖所有意外跳转场景:
1. 处理路由内跳转(返回按钮、导航链接):用setRouteLeaveHook
在你需要阻止跳转的组件(比如有未保存表单的页面)里,在componentDidMount中调用setRouteLeaveHook,它能拦截React Router控制的所有路由切换行为:
componentDidMount() { // 保存路由钩子的清理函数,后续卸载组件时要用 this.leaveHook = this.props.router.setRouteLeaveHook( this.props.route, this.handleRouteLeave ); } handleRouteLeave = (nextLocation) => { // 判断是否有未保存内容,比如表单是否被修改过 if (this.state.hasUnsavedChanges) { return '你有未保存的更改,确定要离开吗?'; } // 没有未保存内容时,返回undefined或true就会允许正常跳转 };
2. 处理页面刷新/关闭标签页:监听beforeunload事件
浏览器原生的beforeunload事件可以捕获页面刷新、关闭标签页这类操作,我们同样在组件挂载时添加监听,卸载时记得移除:
componentDidMount() { // 上面的路由钩子代码... window.addEventListener('beforeunload', this.handleBeforeUnload); } componentWillUnmount() { // 清理路由钩子 this.leaveHook(); // 移除beforeunload事件监听,避免内存泄漏 window.removeEventListener('beforeunload', this.handleBeforeUnload); } handleBeforeUnload = (e) => { if (this.state.hasUnsavedChanges) { // 注意:部分浏览器会忽略自定义文本,显示默认提示,但必须返回值才能触发确认框 e.preventDefault(); e.returnValue = '你有未保存的更改,确定要离开吗?'; return e.returnValue; } };
完整组件示例
把上面的逻辑整合到一个带表单的组件里,大概是这样:
import React, { Component } from 'react'; class EditableForm extends Component { state = { formData: { name: '' }, hasUnsavedChanges: false, }; componentDidMount() { // 设置路由离开钩子 this.leaveHook = this.props.router.setRouteLeaveHook( this.props.route, this.handleRouteLeave ); // 监听页面刷新/关闭事件 window.addEventListener('beforeunload', this.handleBeforeUnload); } componentWillUnmount() { this.leaveHook(); window.removeEventListener('beforeunload', this.handleBeforeUnload); } handleRouteLeave = (nextLocation) => { if (this.state.hasUnsavedChanges) { return '你有未保存的更改,确定要离开吗?'; } }; handleBeforeUnload = (e) => { if (this.state.hasUnsavedChanges) { e.preventDefault(); e.returnValue = '你有未保存的更改,确定要离开吗?'; return e.returnValue; } }; handleInputChange = (e) => { const { name, value } = e.target; this.setState(prevState => ({ formData: { ...prevState.formData, [name]: value }, hasUnsavedChanges: true, })); }; handleSave = () => { // 这里写你的保存逻辑(比如调用接口) this.setState({ hasUnsavedChanges: false }); }; render() { const { formData } = this.state; return ( <div> <input name="name" value={formData.name} onChange={this.handleInputChange} placeholder="输入姓名" /> <button onClick={this.handleSave}>保存</button> </div> ); } } export default EditableForm;
小提醒
beforeunload的提示文本:不同浏览器可能会替换成自己的默认文案,但只要返回非空值,就能触发确认框。- 按需启用:只有当确实存在未保存内容时,才让钩子生效,不然每次跳转都弹提示会影响用户体验。
- 清理工作:一定要在组件卸载时清理路由钩子和事件监听,避免内存泄漏问题。
内容的提问来源于stack exchange,提问作者chan wook
相关产品推荐
相关产品推荐

