使用setRouteLeaveHook使用自定义对话框确认导航

时间:2016-11-22 08:33:33

标签: javascript react-router react-router-redux

我正在尝试使用自定义对话框询问用户确认,然后导航未保存的数据。

关注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布尔值来控制对话框,它还需要handleCancelhandleContinue方法,但我我不确定如何与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连接而不是显示本机浏览器警报?

3 个答案:

答案 0 :(得分:7)

当您致电createHistory时,其中一个选项是getUserConfirmation,其中会提示messagecallback。对于DOM历史记录(browserHistoryhashHistory),getUserConfirmation调用window.confirm,将message传递给它。 callback函数接收返回值window.confirm [0]。

您需要做的是提供自己的getUserConfirmation方法来复制window.confirm。当它被调用时,你应该显示你的模态并根据点击的按钮触发callback

Notification.js

<Notification>组件应根据用户的操作调用提示messagecallback函数。

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)
  }
}

这显然会迫使您创建自己的历史对象。您不能只导入browserHistoryhashHistory,因为它们使用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

答案 1 :(得分:0)

您可以尝试使用react-router-navigation-prompt

这对我有用。

答案 2 :(得分:0)

我使用Prompt中的react-router-domantd中的自定义模式进行了此操作。材质用户界面应具有非常相似的功能。

index.js的{​​{1}}的{​​{1}}中设置对话框:

getUserConfirmation

然后,您使用其中的元素,如果要尝试导航,则使用Router道具提供条件以弹出模态。

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')
)