2016-02-08 14 views
9

Bài viết Confirming Navigation giải thích cách sử dụng hộp xác nhận trình duyệt trong móc chuyển tiếp của bạn. Khỏe. Nhưng tôi muốn sử dụng hộp thoại của riêng mình. Nếu tôi đã sử dụng các phương pháp từ mô-đun history tôi nghĩ rằng điều này là có thể. Có thể thực hiện điều này với setRouteLeaveHook trong bộ phản ứng-bộ định tuyến không?Làm thế nào để sử dụng một thành phần tùy chỉnh với các chuyển đổi tuyến đường phản ứng-bộ định tuyến?

+0

Để hoàn chỉnh: đây là liên kết đó cho thấy làm thế nào để thêm một hộp thoại tùy chỉnh khi sử dụng các mô-đun lịch sử. https://github.com/mjackson/history/blob/master/docs/ConfirmingNavigation.md –

Trả lời

14

Vấn đề cốt lõi là setRouteLeaveHook dự kiến ​​hàm móc sẽ trả lại kết quả đồng bộ. Điều này có nghĩa là bạn không có thời gian để hiển thị thành phần hộp thoại tùy chỉnh, hãy đợi người dùng nhấp vào tùy chọn và rồi trả về kết quả. Vì vậy, chúng tôi cần một cách để chỉ định móc không đồng bộ. Dưới đây là một chức năng hữu ích, tôi đã viết:

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

Dưới đây là một ví dụ về cách sử dụng nó, sử dụng vex để hiển thị một hộp thoại:

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

Cảm ơn Daniel. Bạn có nói rằng nếu setRouteLeaveHook chỉ sử dụng hộp xác nhận trình duyệt mặc định thì điều này được thực hiện đồng bộ không? –

+0

FYI, điều này cũng hoạt động tuyệt vời với hộp thoại thoại/hộp thoại. –

+0

Cảm ơn giải pháp, tôi đã tự hỏi nếu 'router.push (nextLocation)' sẽ luôn hoạt động, không có vấn đề nếu người dùng đang nhấp vào nút quay lại hoặc nút tiến. Có vẻ như 'push' ngụ ý chỉ một hướng, nhưng có lẽ tôi đã nhầm lẫn. – majorBummer

0

Trên đây là lớn trừ khi người dùng quay trở lại trong lịch sử. Nội dung như sau sẽ khắc phục sự cố:

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

Đây là giải pháp của tôi cho tương tự. Tôi đã tạo thành phần hộp thoại tùy chỉnh mà bạn có thể sử dụng để bọc bất kỳ thành phần nào trong ứng dụng của mình. Bạn có thể bọc tiêu đề của bạn và cách này có nó xuất hiện trên tất cả các trang. Giả sử bạn đang sử dụng Biểu mẫu Redux, nhưng bạn có thể chỉ cần thay thế areThereUnsavedChanges bằng một số mã kiểm tra thay đổi biểu mẫu khác. Nó cũng sử dụng phương thức React Bootstrap, một lần nữa bạn có thể thay thế bằng hộp thoại tùy chỉnh của riêng mình.

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

Tôi đã làm cho nó hoạt động bằng cách đặt trạng thái boolean cho dù bạn đã xác nhận điều hướng (sử dụng bộ phản ứng 2.8.x). Vì nó nói trong liên kết mà bạn đã đăng: https://github.com/ReactTraining/react-router/blob/master/docs/guides/ConfirmingNavigation.md

trở lại sai để ngăn chặn một sự chuyển tiếp w/o khiến người sử dụng

Tuy nhiên, họ quên đề cập rằng móc nên không đăng ký là tốt, xem herehere.

Chúng ta có thể sử dụng để thực hiện các giải pháp của riêng của chúng tôi như sau:

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 
}; 
Các vấn đề liên quan