使用 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 布尔值来控制对话框,它还需要一个 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 联系起来而不是显示本机浏览器警报?

当您调用 createHistory 时,它的选项之一是 getUserConfirmation,它需要一个提示符 message 和一个 callback。对于 DOM 历史记录(browserHistoryhashHistory),getUserConfirmation 调用 window.confirm,将其传递给 messagecallback 函数接收 window.confirm [0] 的 return 值。

您需要做的是提供您自己的 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

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

这对我有用。

我使用 react-router-dom 中的 Promptantd 中的自定义模态来完成此操作。 Material UI 应该具有非常相似的功能。

在我的 index.js 中,我在 RoutergetUserConfirmation 中设置了对话框:

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