Реакция условного рисования
Я внедрил компонент Modal, который показывает модальный диалог на экране. Обычно модальное отображение будет условно. Есть два способа сделать это в функции рендеринга:
render(){
...
<Modal show={this.state.showModal}>
// something in modal
</Modal>
}
В модульном компоненте я использую this.props.show для добавления к нему другого класса. Если это неверно, оно добавит отображение: none, чтобы скрыть модальный.
Другой способ:
render(){
...
{ this.state.showModal &&
(<Modal>
// something in modal
</Modal>)
}
}
В этом случае используется showModal
, чтобы решить, добавлять или не добавлять Modal в render.
Что я хочу понять:
- В чем разница между этими двумя способами?
- Один из них лучше другого?
- Есть ли другой способ сделать это?
EDIT: Кажется, что разные люди имеют разные предпочтения. Для меня лично я предпочитаю, что сказал @ErikTheDeveloper. Способность, которая отображает/скрывает Modal, должна оставаться внутри Modal, и когда нам не нужно показывать Modal, мы можем вернуть null в Modal.
Я думаю, может быть, нет определенного ответа, для чего лучше. Может быть, это просто личный выбор?
Ответы
Ответ 1
Я ответил на аналогичный вопрос некоторое время назад, касаясь лучшего способа открытия/закрытия модального
Я провел много времени с Реактивом с тех пор и изучил несколько уроков на этом пути.
Я нашел этот общий подход для работы с модалами: использование полностью контролируемого "немого" компонента, который занимает 3 реквизита.
- show: Boolean - Является ли модальным видимым?
- close: Функция. Модалю требуется обратный вызов, чтобы закрыть себя.
- children: node - содержимое модального
См. React Docs для информации о Контролируемые компоненты
Чтобы ответить на ваш вопрос о различии между ними, это то, что вариант IMO 1 обеспечивает более чистый и гибкий API для работы, тогда как опция 2 более минималистична.
С помощью опции 1 вы можете позаботиться о скрытии/показе, используя CSS или, возвращающий null
из <Modal>
. Я бы рекомендовал вернуть null
, так как модальное содержимое просто не будет отображаться против их рендеринга и "спрятать" их через CSS.
Вариант 2 заставляет более подробный "способ JSX" условного рендеринга, который, по моему мнению, уместен во многих случаях. Однако я чувствую, что концепция модальности заслуживает скрытия/показа, являющегося частью API компонентов <Modal>
(реквизиты/методы/и т.д.)
Зачем пропускать close
prop/callback?
Учитывая, что большинство модалов имеют UX, например, закрытие таких событий, как: нажатие [ESC], нажатие "x", щелчок вне модального и т.д. модальность должна быть проинформирована о том, как "закрыть себя" посредством передачи вниз close
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>
);
}
});
И если вы хотите, чтобы модальные управляли своим собственным состоянием, вы можете обернуть "немой" модальный с помощью более умного компонента и использовать refs и "методы общедоступных компонентов" (хотя я обнаружил, что придерживаться упрощенный подход обычно приводит к меньшей головной боли и сожаления;))
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
Ваш первый пример всегда отображает модальный, но использует CSS, чтобы скрыть/показать его.
Второй пример вставляет только модальный код в DOM при его показе, иначе он вообще не отображается в DOM.
Я предпочитаю не делать это вообще, если это не видно (второй пример), но я не думаю, что это имеет большое значение в любом случае. Второй пример также имеет меньше реквизитов, поэтому Модальный компонент проще.
Ответ 3
Ответ заключается в реализации модального компонента. Я бы ожидал, что метод render
будет использовать show
prop для правильной оптимизации разметки. Вы должны оптимизировать его, чтобы устранить большую часть разметки, когда она не отображается.
Почему?
Внедрение оптимизации в Modal упрощает его использование, другие компоненты не должны знать/беспокоиться о затратах на его рендеринг.
EDIT:
Поскольку мы используем React, стоимость наличия фиктивного модального компонента в v-dom пренебрежимо мала по сравнению со стоимостью его разметки dom. Поэтому, даже если ваши другие компоненты в конечном итоге сохраняют Modal с show = false в их v-dom, это не имеет значения.
Ответ 4
Я предпочитаю второй подход. Несмотря на то, что React минимизирует негативное влияние наличия дополнительных элементов в DOM, всегда бывает хорошей практикой не отображать элементы, которые не предназначены. Я хотел бы расширить это мышление и извлечь логику отображения/скрытия Modal в отдельной функции и вызвать его в render.
render: function(){
...
{this.renderModal()}
},
renderModal: function(){
...
{this.state.showModal && (<Modal />)}
}
Это дает вам гибкость для добавления дополнительных условий в одном месте и делает вашу рендерную функцию малой и легко понятной.
Ответ 5
Это еще один способ сделать это, отредактировать, если модуль:
var Node = require('react-if-comp');
...
var Test = React.createClass({
render: function() {
return <Node if={this.state.showModal}
then={<Modal>// something in modal</Modal>} />;
}
});
Ответ 6
Взгляните на 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>)
// ...