2016-02-08 7 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

धन्यवाद डैनियल। क्या आप कह रहे हैं कि अगर setRouteLeaveHook केवल डिफ़ॉल्ट ब्राउज़र पुष्टिकरण बॉक्स का उपयोग करता है तो इसे सिंक्रनाइज़ तरीके से निष्पादित किया जाता है? –

+0

एफवाईआई, यह संवाद/संवाद पॉलीफिल के साथ भी बहुत अच्छा काम करता है। –

+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 का उपयोग कर एक संवाद बॉक्स प्रदर्शित करने के लिए का एक उदाहरण है। निम्न की तरह कुछ समस्या को ठीक करना चाहिए:

if (!withinHook && nextLocation) { 
    if (nextLocation.action=='POP') { 
     router.goBack() 
    } else { 
     router.push(nextLocation) 
    } 
} 
0

यहां मेरा समाधान है। मैंने एक कस्टम संवाद घटक बनाया है जिसका उपयोग आप अपने ऐप में किसी भी घटक को लपेटने के लिए कर सकते हैं। आप अपने हेडर को लपेट सकते हैं और इस तरह यह सभी पृष्ठों पर मौजूद है। यह मानता है कि आप रेडक्स फॉर्म का उपयोग कर रहे हैं, लेकिन आप बस कुछ अन्य फॉर्म चेंज चेकिंग कोड के साथ areThereUnsavedChanges को प्रतिस्थापित कर सकते हैं। यह रिएक्ट बूटस्ट्रैप मोडल का भी उपयोग करता है, जिसे आप फिर से अपने स्वयं के कस्टम संवाद से प्रतिस्थापित कर सकते हैं।

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

मैं इसे राज्य आप यहां से जाना की पुष्टि की है, इस पर एक बूलियन सेट करके काम बना (प्रतिक्रिया रूटर 2.8.x का उपयोग)। यह लिंक में कहते हैं आप पोस्ट: 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 
}; 
संबंधित मुद्दे