Я реализовал модальный компонент, который показывает модальное диалоговое окно на экране. Обычно модал покажет условно. Есть два способа сделать это в функции рендеринга:

render(){
    ...
    <Modal show={this.state.showModal}>
        // something in modal
    </Modal>
}

В модальном компоненте я использую this.props.show, чтобы добавить другой класс к себе. Когда это ложно, это добавит показ: ни один, чтобы скрыть модальный.

Другой способ такой:

render(){
    ...
    { this.state.showModal &&
        (<Modal>
            // something in modal
        </Modal>)
    }
}

Он использует showModal, чтобы решить, добавлять ли модал в рендер или нет.

То, что я хочу выяснить, это:

  1. В чем разница между этими двумя способами?
  2. Один из них лучше другого?
  3. Есть другой способ сделать это?

РЕДАКТИРОВАТЬ: Кажется, что разные люди имеют разные предпочтения. Я сам предпочитаю то, что сказал @ErikTheDeveloper. Способность показывать / скрывать Модал должна оставаться внутри Модала, и когда нам не нужно показывать Модал, мы можем вернуть нуль в Модале.

Я думаю, может быть, нет определенного ответа, какой путь лучше. Может это просто личный выбор?

21
Zhang Chao 18 Дек 2015 в 06:36

6 ответов

Лучший ответ

Я недавно ответил на похожий вопрос, касающийся лучшего способа открытия / закрытия модального канала.

С тех пор я провел много времени с React и получил несколько уроков.

Я нашел этот общий подход для работы с модалами: использование полностью контролируемого «тупого» компонента, который требует 3 подпорок.

  • show: Boolean - Виден ли модальный?
  • close: Function - модалу нужен обратный вызов, чтобы закрыть себя
  • children: node - Содержимое модального

Информацию о контролируемых компонентах см. В документе React Docs.


Чтобы ответить на ваш вопрос о разнице между ними, заключается в том, что вариант 1 IMO предоставляет более чистый и гибкий API для работы, в то время как вариант 2 более минималистичен.

С помощью опции 1 вы можете позаботиться о скрытии / показе, используя CSS или , возвращая null из <Modal>. Я бы порекомендовал вернуть null, так как модальное содержимое просто не будет отображаться по сравнению с отображением и «скрытием» их с помощью CSS.

Вариант 2 вызывает более подробный «способ JSX» условного рендеринга, который, на мой взгляд, подходит во многих случаях. Тем не менее, я чувствую, что концепция модала заслуживает того, чтобы скрывать / показывать, что она является частью <Modal> API компонентов (props / method / etc ...)


Зачем передавать close реквизит / обратный вызов?

Учитывая, что большинство модалов имеют UX, такие как закрытие событий, таких как: нажатие [ESC], нажатие «x», нажатие вне модального окна и т. Д.… Модал должен быть проинформирован о том, как «закрыть себя», передавая { {X0}} prop / callback в моих примерах ниже.


Примеры кода

// The simple, fully controlled Modal component
const Modal = React.createClass({
  render() {
    const {
      show,     // Boolean - Is the modal visible?
      close,    // Function - The modal needs a function to "close itself"
      children, // node - The contents of the modal 
    } = this.props;
    return !show ? null : (
      <div className="some-class-for-styling">
        <a onClick={close}>x</a>
        {children}
      </div>
    );
  }
});

const UsesModal = React.createClass({
  setEditing(editing) {
    this.setState({editing});
  },

  render() {
    // `editing` could come from anywhere. 
    // Could be derived from props, 
    // or managed locally as state, anywhere really....
    const {editing} = this.state;
    return (
      <div>
        <h1>Some Great Component</h1>
        <a onClick={() => this.setEditing(true)}>Show Modal!</a>
        <Modal show={editing} close={() => this.setEditing(false)}>
          Some great modal content... show based on UsesModal.state.editing
        </Modal>
      </div>
    );
  }
});

И если вы хотите, чтобы модал управлял своим собственным состоянием, вы можете обернуть «тупой» модал чуть более умным компонентом и использовать ссылки и «методы открытых компонентов» (хотя я обнаружил, что придерживаться упрощенного подхода обычно приводит к уменьшению головной боли и сожалений;))

const SmarterModal = React.createClass({
  close() {
    this.setState({show: false});
  },

  open() {
    this.setState({show: true});
  },

  render() {
    const {children} = this.props;
    const {show} = this.state;
    return (
      <Modal show={show} close={this.close}>
        {children}
      </Modal>
    );
  }
});

const UsesSmarterModal = React.createClass({
  render() {
    return (
      <div>
        <h1>Some Great Component</h1>
        <a onClick={() => this.refs.my_smarter_modal.open()}>Show Modal!</a>
        <SmarterModal ref="my_smarter_modal">
          Some great modal content... show based on SmarterModals own internal state
        </SmarterModal>
      </div>
    );
  }
});

Есть несколько способов, которыми можно обернуть простое <Modal>, но я чувствую, что это служит прочной основой, и поток данных играет хорошо, позволяя вычислять / извлекать "модальное открытие" из любой точки, где больше всего получается смысл. Это подход, который я нашел, чтобы работать хорошо.

2
Community 23 Май 2017 в 11:46

Взгляните на https://github.com/fckt/react-layer-stack, позволяет показать / скрыть что-то, что отображается в другой части дерева, но логически связано с компонентом верхнего уровня (что позволяет одновременно использовать переменные из него):

import { Layer, LayerContext } from 'react-layer-stack'
// ... for each `object` in array of `objects`
  const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
  return (
    <Cell {...props}>
        // the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
        <Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
            hideMe, // alias for `hide(modalId)`
            index } // useful to know to set zIndex, for example
            , e) => // access to the arguments (click event data in this example)
          <Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
            <ConfirmationDialog
              title={ 'Delete' }
              message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
              confirmButton={ <Button type="primary">DELETE</Button> }
              onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
              close={ hideMe } />
          </Modal> }
        </Layer>

        // this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
        <LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
          <div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
            <Icon type="trash" />
          </div> }
        </LayerContext>
    </Cell>)
// ...
0
fckt 20 Ноя 2016 в 17:16

Это еще один способ сделать это, реагировать, если модуль:

var Node = require('react-if-comp');
...
var Test = React.createClass({
    render: function() {
        return <Node if={this.state.showModal}
                     then={<Modal>// something in modal</Modal>} />;
    }
});
1
Gordon Freeman 8 Янв 2016 в 23:20

Ваш первый пример всегда отображает модал, но использует CSS, чтобы скрыть / показать его.

Ваш второй пример только вставляет модал в DOM при его показе, иначе он вообще не отображается в DOM.

Я предпочитаю не отображать его вообще, если он не виден (2-й пример), но я не думаю, что это так или иначе имеет большое значение. Во втором примере также меньше реквизита, поэтому модальный компонент проще.

7
Dylan 18 Дек 2015 в 04:00

Ответ заключается в реализации модального компонента. Я ожидаю, что метод render будет использовать реквизит show для правильной оптимизации разметки. Вы должны оптимизировать его, чтобы исключить большую часть разметки, когда она не отображается.

< Сильный > Почему ? Реализация оптимизации в Modal упрощает ее использование, другие компоненты не должны осознавать / беспокоиться о затратах на ее рендеринг.

РЕДАКТИРОВАТЬ: Поскольку мы используем React, стоимость наличия фиктивного компонента Modal в V-DOM незначительна по сравнению со стоимостью его наценки DOM. Таким образом, даже если ваши другие компоненты в конечном итоге сохранят Modal с show = false в своем v-dom, это не имеет значения.

4
hazardous 18 Дек 2015 в 04:38

Я тоже предпочитаю второй подход. Несмотря на то, что React сводит к минимуму негативное влияние наличия дополнительных элементов в DOM, всегда полезно не отображать элементы, которые не предназначены для этого. Я хотел бы расширить это мышление и извлечь логику показа / скрытия модала в отдельной функции и вызвать его в рендере.

render: function(){
   ...
   {this.renderModal()}
},
renderModal: function(){
    ...
    {this.state.showModal && (<Modal />)}
}

Это дает вам гибкость для добавления дополнительных условий в одном месте и делает вашу функцию рендеринга небольшой и легко понятной.

4
coderC 18 Дек 2015 в 05:40