我希望我的 ReactJS 应用程序在离开特定页面时通知用户。特别是提醒他/她执行操作的弹出消息:
“更改已保存,但尚未发布。现在执行吗?”
我应该react-router
全局触发它,还是可以从react页面/组件内完成?
我没有发现关于后者的任何内容,我宁愿避免第一个。当然,除非它是常态,但这让我想知道如何做这样的事情而不必向用户可以访问的每个其他可能的页面添加代码..
欢迎任何见解,谢谢!
我希望我的 ReactJS 应用程序在离开特定页面时通知用户。特别是提醒他/她执行操作的弹出消息:
“更改已保存,但尚未发布。现在执行吗?”
我应该react-router
全局触发它,还是可以从react页面/组件内完成?
我没有发现关于后者的任何内容,我宁愿避免第一个。当然,除非它是常态,但这让我想知道如何做这样的事情而不必向用户可以访问的每个其他可能的页面添加代码..
欢迎任何见解,谢谢!
react-router
v4 引入了一种使用Prompt
. 只需将此添加到您要阻止的组件中:
import { Prompt } from 'react-router'
const MyComponent = () => (
<>
<Prompt
when={shouldBlockNavigation}
message='You have unsaved changes, are you sure you want to leave?'
/>
{/* Component JSX */}
</>
)
这将阻止任何路由,但不会阻止页面刷新或关闭。要阻止它,您需要添加以下内容(根据需要使用适当的 React 生命周期进行更新):
componentDidUpdate = () => {
if (shouldBlockNavigation) {
window.onbeforeunload = () => true
} else {
window.onbeforeunload = undefined
}
}
onbeforeunload有多种浏览器支持。
在 react-routerv2.4.0
或更高版本和之前v4
有几个选项
<Route
path="/home"
onEnter={ auth }
onLeave={ showConfirm }
component={ Home }
>
您可以防止发生转换或在离开带有离开钩子的路线之前提示用户。
const Home = withRouter(
React.createClass({
componentDidMount() {
this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave)
},
routerWillLeave(nextLocation) {
// return false to prevent a transition w/o prompting the user,
// or return a string to allow the user to decide:
// return `null` or nothing to let other hooks to be executed
//
// NOTE: if you return true, other hooks will not be executed!
if (!this.state.isSaved)
return 'Your work is not saved! Are you sure you want to leave?'
},
// ...
})
)
请注意,此示例使用了withRouter
引入的高阶组件v2.4.0.
但是,当手动更改 URL 中的路由时,这些解决方案并不完美
在某种意义上说
对于react-router v4
使用提示或自定义的历史:
然而,在from'react-routerreact-router v4
的帮助下,它更容易实现Prompt
根据文档
迅速的
用于在离开页面之前提示用户。当您的应用程序进入应阻止用户导航离开的状态时(如表单填写一半),渲染
<Prompt>
.import { Prompt } from 'react-router' <Prompt when={formIsHalfFilledOut} message="Are you sure you want to leave?" />
消息:字符串
当用户尝试离开时提示用户的消息。
<Prompt message="Are you sure you want to leave?"/>
消息:功能
将使用用户尝试导航到的下一个位置和操作来调用。返回一个字符串以向用户显示提示或返回 true 以允许转换。
<Prompt message={location => ( `Are you sure you want to go to ${location.pathname}?` )}/>
什么时候:布尔
不是有条件地
<Prompt>
在守卫后面渲染 a ,您可以始终渲染它但通过when={true}
或相应when={false}
地阻止或允许导航。
在您的渲染方法中,您只需根据需要添加文档中提到的内容。
更新:
如果您希望在用户离开页面时执行自定义操作,您可以使用自定义历史记录并配置您的路由器,例如
历史.js
import createBrowserHistory from 'history/createBrowserHistory'
export const history = createBrowserHistory()
...
import { history } from 'path/to/history';
<Router history={history}>
<App/>
</Router>
然后在您的组件中,您可以使用history.block
like
import { history } from 'path/to/history';
class MyComponent extends React.Component {
componentDidMount() {
this.unblock = history.block(targetLocation => {
// take your action here
return false;
});
}
componentWillUnmount() {
this.unblock();
}
render() {
//component render here
}
}
对于react-router
2.4.0+
注意:建议将所有代码迁移到最新版本react-router
以获取所有新内容。
正如react-router 文档中所推荐的:
应该使用withRouter
高阶组件:
我们认为这个新的 HoC 更好、更容易,并将在文档和示例中使用它,但切换并不是硬性要求。
作为文档中的 ES6 示例:
import React from 'react'
import { withRouter } from 'react-router'
const Page = React.createClass({
componentDidMount() {
this.props.router.setRouteLeaveHook(this.props.route, () => {
if (this.state.unsaved)
return 'You have unsaved information, are you sure you want to leave this page?'
})
}
render() {
return <div>Stuff</div>
}
})
export default withRouter(Page)
对于react-router
v3.x
我遇到了同样的问题,我需要对页面上任何未保存更改的确认消息。在我的例子中,我使用的是React Router v3,所以我不能使用<Prompt />
,它是从React Router v4引入的。
我处理“后退按钮点击”和“意外的链接点击”用的组合setRouteLeaveHook
和history.pushState()
,并办理“刷新按钮”与onbeforeunload
事件处理程序。
setRouteLeaveHook ( doc ) & history.pushState ( doc )
仅使用 setRouteLeaveHook 是不够的。出于某种原因,尽管单击“后退按钮”时页面保持不变,但 URL 已更改。
// setRouteLeaveHook returns the unregister method
this.unregisterRouteHook = this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
);
...
routerWillLeave = nextLocation => {
// Using native 'confirm' method to show confirmation message
const result = confirm('Unsaved work will be lost');
if (result) {
// navigation confirmed
return true;
} else {
// navigation canceled, pushing the previous path
window.history.pushState(null, null, this.props.route.path);
return false;
}
};
onbeforeunload (文档)
它用于处理“意外重新加载”按钮
window.onbeforeunload = this.handleOnBeforeUnload;
...
handleOnBeforeUnload = e => {
const message = 'Are you sure?';
e.returnValue = message;
return message;
}
以下是我编写的完整组件
this.props.router
.this.props.route
是从调用组件传递下来的请注意,currentState
作为props传递以具有初始状态并检查任何更改
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { withRouter } from 'react-router';
import Component from '../Component';
import styles from './PreventRouteChange.css';
class PreventRouteChange extends Component {
constructor(props) {
super(props);
this.state = {
// initialize the initial state to check any change
initialState: _.cloneDeep(props.currentState),
hookMounted: false
};
}
componentDidUpdate() {
// I used the library called 'lodash'
// but you can use your own way to check any unsaved changed
const unsaved = !_.isEqual(
this.state.initialState,
this.props.currentState
);
if (!unsaved && this.state.hookMounted) {
// unregister hooks
this.setState({ hookMounted: false });
this.unregisterRouteHook();
window.onbeforeunload = null;
} else if (unsaved && !this.state.hookMounted) {
// register hooks
this.setState({ hookMounted: true });
this.unregisterRouteHook = this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
);
window.onbeforeunload = this.handleOnBeforeUnload;
}
}
componentWillUnmount() {
// unregister onbeforeunload event handler
window.onbeforeunload = null;
}
handleOnBeforeUnload = e => {
const message = 'Are you sure?';
e.returnValue = message;
return message;
};
routerWillLeave = nextLocation => {
const result = confirm('Unsaved work will be lost');
if (result) {
return true;
} else {
window.history.pushState(null, null, this.props.route.path);
if (this.formStartEle) {
this.moveTo.move(this.formStartEle);
}
return false;
}
};
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
PreventRouteChange.propTypes = propTypes;
export default withRouter(PreventRouteChange);
如果有任何问题,请告诉我:)
使用 history.listen
例如像下面这样:
在您的组件中,
componentWillMount() {
this.props.history.listen(() => {
// Detecting, user has changed URL
console.info(this.props.history.location.pathname);
});
}