Pertanyaan Bagaimana cara menggunakan komponen kustom dengan transisi rute reaktor-router?


Artikel Mengonfirmasi Navigasi menjelaskan cara menggunakan kotak konfirmasi browser di hook transisi Anda. Baik. Tapi saya ingin menggunakan kotak Dialog saya sendiri. Jika saya menggunakan metode dari history modul saya pikir ini mungkin. Apakah mungkin untuk melakukan ini dengan setRouteLeaveHook di bereaksi-router?


10
2018-02-08 17:11


asal


Jawaban:


Masalah utamanya adalah itu setRouteLeaveHook mengharapkan fungsi hook untuk mengembalikan hasilnya serentak. Ini berarti Anda tidak memiliki waktu untuk menampilkan komponen dialog khusus, menunggu pengguna untuk mengklik suatu opsi, dan kemudian kembalikan hasilnya. Jadi kita perlu cara untuk menentukan suatu asynchronous menghubungkan. Berikut adalah fungsi utilitas yang saya tulis:

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

Berikut ini contoh cara menggunakannya, menggunakan menyusahkan untuk menampilkan kotak dialog:

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

15
2018-03-04 10:17



Saya membuatnya bekerja dengan mengatur keadaan boolean apakah Anda telah dikonfirmasi untuk menavigasi pergi (menggunakan reaktor 2.8.x). Seperti yang tertulis di tautan yang Anda poskan: https://github.com/ReactTraining/react-router/blob/master/docs/guides/ConfirmingNavigation.md

kembalikan false untuk mencegah transisi tanpa mendorong pengguna

Namun, mereka lupa menyebutkan bahwa hook seharusnya tidak terdaftar juga, lihat sini dan sini.

Kita dapat menggunakan ini untuk menerapkan solusi kita sendiri sebagai berikut:

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

1
2018-01-27 23:13



Posting solusi saya untuk mencegat tombol kembali atau bahkan perubahan rute. Ini berfungsi dengan React-router 2.8 atau lebih tinggi. Atau bahkan dengan denganRouter

import React, {PropTypes as T} from 'react';

...
componentWillMount() {
        this.context.router.setRouteLeaveHook(this.props.route, this.routerWillLeaveCallback.bind(this));
    }

    routerWillLeaveCallback(nextLocation) {
        let showModal = this.state.unsavedChanges;
        if (showModal) {
            this.setState({
                openUnsavedDialog: true,
                unsavedResolveCallback: Promise.resolve
            });
            return false;
        }
        return true;
    }
}


YourComponent.contextTypes = {
    router: T.object.isRequired
};

1
2018-03-22 04:46



Di atas sangat bagus kecuali ketika pengguna kembali dalam sejarah. Sesuatu seperti ini harus memperbaiki masalah:

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

0
2017-08-19 06:09



Inilah solusi saya untuk hal yang sama. Saya membuat komponen dialog khusus yang dapat Anda gunakan untuk membungkus komponen apa pun di aplikasi Anda. Anda dapat membungkus header Anda dan cara ini ada pada semua halaman. Ini mengasumsikan Anda menggunakan Redux Form, tetapi Anda dapat dengan mudah mengganti areThereUnsavedChanges dengan beberapa kode pengecekan perubahan bentuk lainnya. Ia juga menggunakan modal React Bootstrap, yang sekali lagi Anda dapat mengganti dengan dialog kustom Anda sendiri.

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
2017-11-17 20:40