【问题标题】:Confirming navigation with with custom dialog using setRouteLeaveHook使用 setRouteLeaveHook 使用自定义对话框确认导航
【发布时间】:2017-04-05 20:18:31
【问题描述】:

我正在尝试使用自定义对话框在使用未保存的数据导航之前要求用户确认。

按照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>
    );
  }
}

我可以从父组件调用它,我在渲染方法中有这个:

<Notification open={open} handleCancel={this.handleCancel} handleContinue={this.handleContinue}/>

基本上我的问题是如何将其与routerWillLeave 联系起来而不是显示本机浏览器警报?

【问题讨论】:

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


    【解决方案1】:

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

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

    Notification.js

    &lt;Notification&gt; 组件应根据用户的操作采取提示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-redux 将存储与历史同步,即 const history = syncHistoryWithStore(browserHistory, store)。如何附加到此历史记录 getModalConfirmation?
    • 您必须创建自己的历史记录,而不是使用browserHistory。如果您需要在整个项目中使用历史实例,您可以修改我上面包含的createConfirmationHistory 以导入您的getUserConfirmation,创建一个历史实例,然后导出该实例。这就是browserHistory 模块的作用。 github.com/ReactTraining/react-router/blob/v3.0.0/modules/… 如果不清楚,我可以链接一个我正在谈论的例子。
    • 我没有测试过这个,你可能需要修改它,但这是一般的想法gist.github.com/pshrmn/7a7b9a4edddc749d353425919e48707e
    【解决方案2】:

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

    这对我有用。

    【讨论】:

      【解决方案3】:

      我使用来自react-router-domPrompt 和来自antd 的自定义模态来做到这一点。 Material UI 应该具有非常相似的功能。

      在我的index.js 中,我在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')
      )
      

      然后,如果您尝试导航离开,则在其中使用要提示的组件,使用 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>
          )
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-06-05
        • 1970-01-01
        • 2016-10-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-29
        • 1970-01-01
        相关资源
        最近更新 更多