2016-02-08 13 views
9

記事Confirming Navigationは、トランジションフックでブラウザの確認ボックスを使用する方法を説明しています。ファイン。しかし、私は自分自身のダイアログボックスを使いたいです。私がhistoryモジュールのメソッドを使用するなら、これは可能だと思います。反応ルータのsetRouteLeaveHookでこれを行うことは可能ですか?反応ルータのルートトランジションでカスタムコンポーネントを使用するにはどうすればいいですか?

+0

完全性について:これは履歴モジュールを使用するときにカスタムダイアログボックスを追加する方法を示したリンクです。 https://github.com/mjackson/history/blob/master/docs/ConfirmingNavigation.md –

答えて

14

setRouteLeaveHookは、結果がと同時にのフック関数が返すことを期待しています。つまり、カスタムダイアログコンポーネントを表示する時間がなく、ユーザーがオプションをクリックするのを待ってから、、次にが結果を返します。したがって、非同期フックを指定する方法が必要です。ここで私が書いたユーティリティ関数です:

componentWillMount() { 
    setAsyncRouteLeaveHook(this.context.router, this.props.route, this.routerWillLeave) 
} 
​ 
routerWillLeave() { 
    return new Promise((resolve, reject) => { 
    if (!this.state.textValue) { 
     // No unsaved changes -- leave 
     resolve(true) 
    } else { 
     // Unsaved changes -- ask for confirmation 
     vex.dialog.confirm({ 
     message: 'There are unsaved changes. Leave anyway?' + nextLocation, 
     callback: result => resolve(result) 
     }) 
    } 
    }) 
} 
+0

Danielに感謝します。 setRouteLeaveHookがデフォルトのブラウザの確認ボックスを使用する場合、これは同期的に実行されると言っていますか? –

+0

FYI、これはdialog/dialog polyfillでもうまくいきます。 –

+0

解決してくれてありがとう、私は 'router.push(nextLocation)'がいつも動くかどうか疑問に思っていた。 「プッシュ」はただ一つの方向を示唆しているようだが、おそらく私は間違っている。 – majorBummer

0

上記は、ユーザーが履歴に戻ったときを除いて素晴らしいです:

ここ
// Asynchronous version of `setRouteLeaveHook`. 
// Instead of synchronously returning a result, the hook is expected to 
// return a promise. 
function setAsyncRouteLeaveHook(router, route, hook) { 
    let withinHook = false 
    let finalResult = undefined 
    let finalResultSet = false 
    router.setRouteLeaveHook(route, nextLocation => { 
    withinHook = true 
    if (!finalResultSet) { 
     hook(nextLocation).then(result => { 
     finalResult = result 
     finalResultSet = true 
     if (!withinHook && nextLocation) { 
      // Re-schedule the navigation 
      router.push(nextLocation) 
     } 
     }) 
    } 
    let result = finalResultSet ? finalResult : false 
    withinHook = false 
    finalResult = undefined 
    finalResultSet = false 
    return result 
    }) 
} 

は、ダイアログボックスを表示するvexを使用して、それを使用する方法の例です。次のようなものが問題を解決するはずです:

0

私の解決方法は同じです。私はあなたのアプリで任意のコンポーネントをラップするために使用できるカスタムダイアログコンポーネントを作った。ヘッダーをラップして、この方法ですべてのページに表示させることができます。 Reduxフォームを使用していることを前提としていますが、areThereUnsavedChangesを他のフォームチェンジチェックコードに置き換えることができます。それはReact Bootstrapモーダルも使用します。これはあなた自身のカスタムダイアログで置き換えることができます。

import React, { Component } from 'react' 
import { connect } from 'react-redux' 
import { withRouter, browserHistory } from 'react-router' 
import { translate } from 'react-i18next' 
import { Button, Modal, Row, Col } from 'react-bootstrap' 

// have to use this global var, because setState does things at unpredictable times and dialog gets presented twice 
let navConfirmed = false 

@withRouter 
@connect(
    state => ({ form: state.form }) 
) 
export default class UnsavedFormModal extends Component { 
    constructor(props) { 
    super(props) 
    this.areThereUnsavedChanges = this.areThereUnsavedChanges.bind(this) 
    this.state = ({ unsavedFormDialog: false }) 
    } 

    areThereUnsavedChanges() { 
    return this.props.form && Object.values(this.props.form).length > 0 && 
     Object.values(this.props.form) 
     .findIndex(frm => (Object.values(frm) 
      .findIndex(field => field && field.initial && field.initial !== field.value) !== -1)) !== -1 
    } 

    render() { 
    const moveForward =() => { 
     this.setState({ unsavedFormDialog: false }) 
     navConfirmed = true 
     browserHistory.push(this.state.nextLocation.pathname) 
    } 
    const onHide =() => this.setState({ unsavedFormDialog: false }) 

    if (this.areThereUnsavedChanges() && this.props.router && this.props.routes && this.props.routes.length > 0) { 
     this.props.router.setRouteLeaveHook(this.props.routes[this.props.routes.length - 1], (nextLocation) => { 
     if (navConfirmed || !this.areThereUnsavedChanges()) { 
      navConfirmed = false 
      return true 
     } else { 
      this.setState({ unsavedFormDialog: true, nextLocation: nextLocation }) 
      return false 
     } 
     }) 
    } 

    return (
     <div> 
     {this.props.children} 
     <Modal show={this.state.unsavedFormDialog} onHide={onHide} bsSize="sm" aria-labelledby="contained-modal-title-md"> 
      <Modal.Header> 
      <Modal.Title id="contained-modal-title-md">WARNING: unsaved changes</Modal.Title> 
      </Modal.Header> 
      <Modal.Body> 
      Are you sure you want to leave the page without saving changes to the form? 
      <Row> 
       <Col xs={6}><Button block onClick={onHide}>Cancel</Button></Col> 
       <Col xs={6}><Button block onClick={moveForward}>OK</Button></Col> 
      </Row> 
      </Modal.Body> 
     </Modal> 
     </div> 
    ) 
    } 
} 
0

私は、(react-router 2.8.xを使用して)ナビゲートすることを確認したかどうかをブール値で設定しました。ただし、ユーザー

を促すW/O、彼らはフックが同様に未登録であることを言及するのを忘れての移行を防ぐために偽 https://github.com/ReactTraining/react-router/blob/master/docs/guides/ConfirmingNavigation.md

が復帰、:それはあなたが投稿リンクで言うようにhereおよびhereを参照してください。次のように

我々は独自のソリューションを実装するためにこれを使用することができます。

class YourComponent extends Component { 
    constructor() { 
    super(); 

    const {route} = this.props; 
    const {router} = this.context; 

    this.onCancel = this.onCancel.bind(this); 
    this.onConfirm = this.onConfirm.bind(this); 

    this.unregisterLeaveHook = router.setRouteLeaveHook(
     route, 
     this.routerWillLeave.bind(this) 
    ); 
    } 

    componentWillUnmount() { 
    this.unregisterLeaveHook(); 
    } 

    routerWillLeave() { 
    const {hasConfirmed} = this.state; 
    if (!hasConfirmed) { 
     this.setState({showConfirmModal: true}); 

     // Cancel route change 
     return false; 
    } 

    // User has confirmed. Navigate away 
    return true; 
    } 

    onCancel() { 
    this.setState({showConfirmModal: false}); 
    } 

    onConfirm() { 
    this.setState({hasConfirmed: true, showConfirmModal: true}, function() { 
     this.context.router.goBack(); 
    }.bind(this)); 
    } 

    render() { 
    const {showConfirmModal} = this.state; 

    return (
     <ConfirmModal 
     isOpen={showConfirmModal} 
     onCancel={this.onCancel} 
     onConfirm={this.onConfirm} /> 
    ); 
    } 
} 

YourComponent.contextTypes = { 
    router: routerShape 
}; 
関連する問題