我希望我的ReactJS应用程序在离开特定页面时通知用户。特别是弹出消息,提醒他/她做一个动作:
“更改已保存,但尚未发布。请立即执行此操作吗?”
我是否应该在react-router
全局触发此问题,或者这是否可以在反应页/组件中完成?
我在后者身上找不到任何东西,我宁愿避开第一个。除非它当然是标准,但这让我想知道如何做这样的事情,而不必将代码添加到用户可以去的每个其他可能的页面..
欢迎任何见解,谢谢!
答案 0 :(得分:59)
react-router
v4引入了一种使用Prompt
阻止导航的新方法。只需将其添加到您要阻止的组件:
import { Prompt } from 'react-router'
const MyComponent = () => (
<React.Fragment>
<Prompt
when={shouldBlockNavigation}
message='You have unsaved changes, are you sure you want to leave?'
/>
{/* Component JSX */}
</React.Fragment>
)
这将阻止任何路由,但不会阻止页面刷新或关闭。要阻止它,您需要添加它(根据需要使用适当的React生命周期进行更新):
componentDidUpdate = () => {
if (shouldBlockNavigation) {
window.onbeforeunload = () => true
} else {
window.onbeforeunload = undefined
}
}
onbeforeunload得到浏览器的各种支持。
答案 1 :(得分:26)
在react-router v2.4.0
或更高版本以及v4
之前有几个选项
onLeave
for Route
强> <Route
path="/home"
onEnter={ auth }
onLeave={ showConfirm }
component={ Home }
>
setRouteLeaveHook
for componentDidMount
您可以在离开带有假挂钩的路线之前阻止转换发生或提示用户。
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
使用提示或自定义历史记录:
但是在react-router v4
中,在#strong> 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?"/>
消息:func
将使用用户所在的下一个位置和操作进行调用 试图导航到。返回一个字符串以显示提示 user或true表示允许转换。
<Prompt message={location => ( `Are you sure you want to go to ${location.pathname}?` )}/>
时:bool
而不是有条件地在守卫后面渲染
<Prompt>
可以随时呈现它,但可以将when={true}
或when={false}
传递给 相应地阻止或允许导航。
在您的渲染方法中,您只需根据需要添加文档中提到的内容。
<强>更新强>
如果您希望在使用离开页面时执行自定义操作,则可以使用自定义历史记录并配置路由器
<强> history.js 强>
import createBrowserHistory from 'history/createBrowserHistory'
export const history = createBrowserHistory()
...
import { history } from 'path/to/history';
<Router history={history}>
<App/>
</Router>
然后在您的组件中,您可以使用history.block
之类的
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
}
}
答案 2 :(得分:23)
适用于react-router
2.4.0 +
注意:建议您将所有代码迁移到最新的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)
答案 3 :(得分:3)
适用于react-router
v3.x
我遇到了同样的问题,我需要在页面上进行任何未保存的更改的确认消息。在我的情况下,我使用 React Router v3 ,因此我无法使用从{strong> React Router v4 引入的<Prompt />
。
我处理后退按钮点击&#39;和&#39;意外链接点击&#39;结合使用setRouteLeaveHook
和history.pushState()
,并处理重新加载按钮&#39;使用onbeforeunload
事件处理程序。
setRouteLeaveHook (doc)&amp; history.pushState (doc)
仅使用setRouteLeaveHook是不够的。出于某种原因,虽然页面保持不变,但是在“后退按钮”时,网址已更改。点击了。
// 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 (doc)
它用于处理意外重新加载&#39;按钮
window.onbeforeunload = this.handleOnBeforeUnload;
...
handleOnBeforeUnload = e => {
const message = 'Are you sure?';
e.returnValue = message;
return message;
}
以下是我撰写的完整组件
this.props.router
。 this.props.route
从调用组件传递下来请注意currentState
作为prop传递给初始状态并检查任何更改
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);
如果有任何问题,请告诉我:)
答案 4 :(得分:2)
react-router
v0.13.x与react
v0.13.x:
使用willTransitionTo()
和willTransitionFrom()
静态方法可以实现这一点。对于较新版本,请参阅下面的其他答案。
您可以在路由处理程序中定义一些静态方法,这些方法将在路由转换期间调用。
willTransitionTo(transition, params, query, callback)
当处理程序即将渲染时调用,使您有机会中止或重定向转换。您可以在执行某些异步工作时暂停转换,并在完成后调用回调(错误),或者在您的参数列表中省略回调,它将为您调用。
willTransitionFrom(transition, component, callback)
在转换活动路线时调用,为您提供中止转换的机会。组件是当前组件,您可能需要它来检查其状态以决定是否允许转换(如表单字段)。
实施例
var Settings = React.createClass({ statics: { willTransitionTo: function (transition, params, query, callback) { auth.isLoggedIn((isLoggedIn) => { transition.abort(); callback(); }); }, willTransitionFrom: function (transition, component) { if (component.formHasUnsavedData()) { if (!confirm('You have unsaved information,'+ 'are you sure you want to leave this page?')) { transition.abort(); } } } } //... });
对于react-router
1.0.0-rc1 react
v0.14.x或更高版本:
这应该可以使用routerWillLeave
生命周期钩子。对于旧版本,请参阅上面的答案。
要安装此挂钩,请在其中一个路由组件中使用Lifecycle mixin。
import { Lifecycle } from 'react-router' const Home = React.createClass({ // Assuming Home is a route component, it may use the // Lifecycle mixin to get a routerWillLeave method. mixins: [ Lifecycle ], routerWillLeave(nextLocation) { if (!this.state.isSaved) return 'Your work is not saved! Are you sure you want to leave?' }, // ... })
的东西。可能会在最终版本发布之前发生变化。
答案 5 :(得分:1)
您可以使用此提示。
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link, Prompt } from "react-router-dom";
function PreventingTransitionsExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Form</Link>
</li>
<li>
<Link to="/one">One</Link>
</li>
<li>
<Link to="/two">Two</Link>
</li>
</ul>
<Route path="/" exact component={Form} />
<Route path="/one" render={() => <h3>One</h3>} />
<Route path="/two" render={() => <h3>Two</h3>} />
</div>
</Router>
);
}
class Form extends Component {
state = { isBlocking: false };
render() {
let { isBlocking } = this.state;
return (
<form
onSubmit={event => {
event.preventDefault();
event.target.reset();
this.setState({
isBlocking: false
});
}}
>
<Prompt
when={isBlocking}
message={location =>
`Are you sure you want to go to ${location.pathname}`
}
/>
<p>
Blocking?{" "}
{isBlocking ? "Yes, click a link or the back button" : "Nope"}
</p>
<p>
<input
size="50"
placeholder="type something to block transitions"
onChange={event => {
this.setState({
isBlocking: event.target.value.length > 0
});
}}
/>
</p>
<p>
<button>Submit to stop blocking</button>
</p>
</form>
);
}
}
export default PreventingTransitionsExample;
答案 6 :(得分:0)
使用history.listen
例如如下所示:
在您的组件中,
componentWillMount() {
this.props.history.listen(() => {
// Detecting, user has changed URL
console.info(this.props.history.location.pathname);
});
}
答案 7 :(得分:0)
也许您可以使用 componentWillUnmount()
在用户离开页面之前执行任何操作。如果您正在使用功能组件,那么您可以使用 useEffect()
钩子做同样的事情。钩子接受一个返回 Destructor
的函数,这类似于 componentWillUnmount()
可以做的。
信用转到this article