使用 setRouteLeaveHook 通过自定义对话框确认导航
Confirming navigation with with custom dialog using setRouteLeaveHook
我正在尝试使用自定义对话框在使用未保存的数据导航之前要求用户进行确认。
继docs之后我有:
componentDidMount() {
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
}
但不是
routerWillLeave(nextLocation) {
if (!this.props.pristine) {
return 'You have unsaved information, are you sure you want to leave this page?'
}
我有
routerWillLeave(nextLocation) {
if (!this.props.pristine) {
this.setState({open: true})
this.forceUpdatate() //necessary or else render won't be called to open dialog
}
我正在使用的对话框组件来自 material-ui,它只需要一个 open
布尔值来控制对话框,它还需要一个 handleCancel
和 handleContinue
方法,但我不确定如何将它与 routerWillLeave
.
联系起来
handleCancel
方法很简单,就是关闭对话框:
handleCancel() {
this.setState({open: false})
};
我已将对话框组件包装在名为 Notification
的组件中
export default class Notification extends React.Component {
render() {
const { open, handleCancel, handleContinue } = this.props
const actions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={handleCancel}
/>,
<FlatButton
label="Continue"
primary={true}
onTouchTap={handleContinue}
/>,
];
return (
<div>
<Dialog
actions={actions}
modal={false}
open={open}
>
You have unsaved data. Discard changes?
</Dialog>
</div>
);
}
}
我可以从父组件调用它,我在 render 方法中有这个:
<Notification open={open} handleCancel={this.handleCancel} handleContinue={this.handleContinue}/>
基本上我的问题是如何将它与 routerWillLeave
联系起来而不是显示本机浏览器警报?
当您调用 createHistory
时,它的选项之一是 getUserConfirmation
,它需要一个提示符 message
和一个 callback
。对于 DOM 历史记录(browserHistory
和 hashHistory
),getUserConfirmation
调用 window.confirm
,将其传递给 message
。 callback
函数接收 window.confirm
[0] 的 return 值。
您需要做的是提供您自己的 getUserConfirmation
方法来复制 window.confirm
。当它被调用时,您应该显示您的模态并根据单击哪个按钮触发 callback
。
Notification.js
<Notification>
组件应该根据用户的操作采取提示message
和callback
函数来调用。
class Notification extends React.Component {
contructor(props) {
this.state = {
open: false
}
}
handleCancel() {
this.props.callback(false)
this.setState({ open: false })
}
handleContinue() {
this.props.callback(true)
this.setState({ open: false })
}
render() {
const { message } = this.props
const { open } = this.state
const actions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={this.handleCancel.bind(this)}
/>,
<FlatButton
label="Continue"
primary={true}
onTouchTap={this.handleContinue.bind(this)}
/>,
];
return (
<div>
<Dialog
actions={actions}
modal={true}
open={open}
>
{message}
</Dialog>
</div>
);
}
}
ModalConfirmation.js
确认模式实际上不是您UI的一部分,这就是为什么我在单独的渲染进程中渲染它而不是应用程序的其余部分.
import React from 'react'
import ReactDOM from 'react-dom'
import Notification from './components/Notification'
export default function = (holderID) => {
var modalHolder = document.getElementById(holderID)
return function ModalUserConfirmation(message, callback) {
ReactDOM.render((
<Notification open={true} message={message} callback={callback} />
), modalHolder)
}
}
这显然会迫使您创建自己的历史对象。您不能只导入 browserHistory
或 hashHistory
,因为它们使用 window.confirm
。幸运的是,创建自己的历史是微不足道的。这与 browserHistory
[1] 中使用的代码基本相同,但它传递 createBrowserHistory
您的 getUserConfirmation
函数。
createConfirmationHistory.js
import createBrowserHistory from 'history/lib/createBrowserHistory'
import createRouterHistory from './createRouterHistory'
export default function(getUserConfirmation) {
return createRouterHistory(createBrowserHistory({
getUserConfirmation
})
}
index.js
最后,你需要把这些放在一起。
import createHistory from './createConfirmationHistory'
import ModalConfirmation from './ModalConfirmation'
const getModalConfirmation = ModalConfirmation('modal-holder')
const history = createHistory(getModalConfirmation)
ReactDOM.render((
<Router history={history}>
// ...
</Router>
), document.getElementById('root')
如果你想使用历史单例,你必须稍微重构一下,否则它应该可以工作。 (不过我还没有实际测试过)。
[0] https://github.com/mjackson/history/blob/v2.x/modules/DOMUtils.js#L38-L40
[1] https://github.com/ReactTraining/react-router/blob/master/modules/browserHistory.js
您可以尝试使用 react-router-navigation-prompt。
这对我有用。
我使用 react-router-dom
中的 Prompt
和 antd
中的自定义模态来完成此操作。 Material UI 应该具有非常相似的功能。
在我的 index.js
中,我在 Router
的 getUserConfirmation
中设置了对话框:
import { MemoryRouter as Router } from 'react-router-dom'
import { Modal } from 'antd'
import App from './App'
const { confirm } = Modal
const confirmNavigation = (message, callback) => {
confirm({
title: message,
onOk() {
callback(true)
},
onCancel() {
callback(false)
}
})
}
ReactDOM.render(
<Router getUserConfirmation={confirmNavigation}>
<App />
</Router>
document.getElementById('root')
)
然后在其中使用要在尝试导航离开时提示的组件,使用 when
属性给出弹出模式的条件。
import { Prompt } from 'react-router-dom'
class MyComponent extends React.Component {
render() {
return (
<div>
<Prompt
when={myDataHasNotSavedYet}
message="This will lose your changes. Are you sure want leave the page?"
/>
<RestOfMyComponent/>
</div>
)
}
}
我正在尝试使用自定义对话框在使用未保存的数据导航之前要求用户进行确认。
继docs之后我有:
componentDidMount() {
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
}
但不是
routerWillLeave(nextLocation) {
if (!this.props.pristine) {
return 'You have unsaved information, are you sure you want to leave this page?'
}
我有
routerWillLeave(nextLocation) {
if (!this.props.pristine) {
this.setState({open: true})
this.forceUpdatate() //necessary or else render won't be called to open dialog
}
我正在使用的对话框组件来自 material-ui,它只需要一个 open
布尔值来控制对话框,它还需要一个 handleCancel
和 handleContinue
方法,但我不确定如何将它与 routerWillLeave
.
handleCancel
方法很简单,就是关闭对话框:
handleCancel() {
this.setState({open: false})
};
我已将对话框组件包装在名为 Notification
export default class Notification extends React.Component {
render() {
const { open, handleCancel, handleContinue } = this.props
const actions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={handleCancel}
/>,
<FlatButton
label="Continue"
primary={true}
onTouchTap={handleContinue}
/>,
];
return (
<div>
<Dialog
actions={actions}
modal={false}
open={open}
>
You have unsaved data. Discard changes?
</Dialog>
</div>
);
}
}
我可以从父组件调用它,我在 render 方法中有这个:
<Notification open={open} handleCancel={this.handleCancel} handleContinue={this.handleContinue}/>
基本上我的问题是如何将它与 routerWillLeave
联系起来而不是显示本机浏览器警报?
当您调用 createHistory
时,它的选项之一是 getUserConfirmation
,它需要一个提示符 message
和一个 callback
。对于 DOM 历史记录(browserHistory
和 hashHistory
),getUserConfirmation
调用 window.confirm
,将其传递给 message
。 callback
函数接收 window.confirm
[0] 的 return 值。
您需要做的是提供您自己的 getUserConfirmation
方法来复制 window.confirm
。当它被调用时,您应该显示您的模态并根据单击哪个按钮触发 callback
。
Notification.js
<Notification>
组件应该根据用户的操作采取提示message
和callback
函数来调用。
class Notification extends React.Component {
contructor(props) {
this.state = {
open: false
}
}
handleCancel() {
this.props.callback(false)
this.setState({ open: false })
}
handleContinue() {
this.props.callback(true)
this.setState({ open: false })
}
render() {
const { message } = this.props
const { open } = this.state
const actions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={this.handleCancel.bind(this)}
/>,
<FlatButton
label="Continue"
primary={true}
onTouchTap={this.handleContinue.bind(this)}
/>,
];
return (
<div>
<Dialog
actions={actions}
modal={true}
open={open}
>
{message}
</Dialog>
</div>
);
}
}
ModalConfirmation.js
确认模式实际上不是您UI的一部分,这就是为什么我在单独的渲染进程中渲染它而不是应用程序的其余部分.
import React from 'react'
import ReactDOM from 'react-dom'
import Notification from './components/Notification'
export default function = (holderID) => {
var modalHolder = document.getElementById(holderID)
return function ModalUserConfirmation(message, callback) {
ReactDOM.render((
<Notification open={true} message={message} callback={callback} />
), modalHolder)
}
}
这显然会迫使您创建自己的历史对象。您不能只导入 browserHistory
或 hashHistory
,因为它们使用 window.confirm
。幸运的是,创建自己的历史是微不足道的。这与 browserHistory
[1] 中使用的代码基本相同,但它传递 createBrowserHistory
您的 getUserConfirmation
函数。
createConfirmationHistory.js
import createBrowserHistory from 'history/lib/createBrowserHistory'
import createRouterHistory from './createRouterHistory'
export default function(getUserConfirmation) {
return createRouterHistory(createBrowserHistory({
getUserConfirmation
})
}
index.js
最后,你需要把这些放在一起。
import createHistory from './createConfirmationHistory'
import ModalConfirmation from './ModalConfirmation'
const getModalConfirmation = ModalConfirmation('modal-holder')
const history = createHistory(getModalConfirmation)
ReactDOM.render((
<Router history={history}>
// ...
</Router>
), document.getElementById('root')
如果你想使用历史单例,你必须稍微重构一下,否则它应该可以工作。 (不过我还没有实际测试过)。
[0] https://github.com/mjackson/history/blob/v2.x/modules/DOMUtils.js#L38-L40
[1] https://github.com/ReactTraining/react-router/blob/master/modules/browserHistory.js
您可以尝试使用 react-router-navigation-prompt。
这对我有用。
我使用 react-router-dom
中的 Prompt
和 antd
中的自定义模态来完成此操作。 Material UI 应该具有非常相似的功能。
在我的 index.js
中,我在 Router
的 getUserConfirmation
中设置了对话框:
import { MemoryRouter as Router } from 'react-router-dom'
import { Modal } from 'antd'
import App from './App'
const { confirm } = Modal
const confirmNavigation = (message, callback) => {
confirm({
title: message,
onOk() {
callback(true)
},
onCancel() {
callback(false)
}
})
}
ReactDOM.render(
<Router getUserConfirmation={confirmNavigation}>
<App />
</Router>
document.getElementById('root')
)
然后在其中使用要在尝试导航离开时提示的组件,使用 when
属性给出弹出模式的条件。
import { Prompt } from 'react-router-dom'
class MyComponent extends React.Component {
render() {
return (
<div>
<Prompt
when={myDataHasNotSavedYet}
message="This will lose your changes. Are you sure want leave the page?"
/>
<RestOfMyComponent/>
</div>
)
}
}