Показать/скрыть компоненты ReactJS, не потеряв их внутреннего состояния?
Я скрывал/показывал компоненты реакции, не создавая их, например:
render: function() {
var partial;
if (this.state.currentPage === 'home') {
partial = <Home />;
} else if (this.state.currentPage === 'bio') {
partial = <Bio />;
} else {
partial = <h1>Not found</h1>
}
return (
<div>
<div>I am a menu that stays here</div>
<a href="#/home">Home</a> <a href="#/bio">Bio</a>
{partial}
</div>
);
}
но просто скажите, что компонент <Bio/>
имеет много внутреннего состояния. Каждый раз, когда я воссоздаю компонент, он теряет его внутреннее состояние и сбрасывает его в исходное состояние.
Я знаю, конечно, что я могу хранить данные для него где-то и передавать его через реквизит или просто глобально получить к нему доступ, но эти данные действительно не нужны для жизни вне компонента. Я мог бы скрыть/показать компоненты с помощью CSS (display:none
), но я бы предпочел скрыть/показать их, как указано выше.
Какая самая лучшая практика здесь?
EDIT: Возможно, лучший способ сформулировать проблему - использовать пример:
Игнорировать React и предположим, что вы просто использовали настольное приложение, в котором был диалог конфигурации с компонентом Tab под названием A, который имеет 2 вкладки с именами 1 и 2.
Скажите, что на вкладке A.1 есть текстовое поле электронной почты, и вы заполняете свой адрес электронной почты. Затем вы нажимаете на вкладку A.2 на секунду, а затем переходите к вкладке A.1. Что случилось? Ваш адрес электронной почты больше не будет, это было бы без reset, потому что внутреннее состояние не хранилось нигде.
Интернализация состояния работает, как предложено в одном из ответов ниже, но только для компонента и его непосредственных детей. Если у вас есть компоненты, произвольно вложенные в другие компоненты, скажите вкладки в вкладках в вкладках, единственный способ сохранить их внутреннее состояние - либо экстеризировать его где-нибудь, либо использовать подход display:none
, который фактически сохраняет все дочерние компоненты вокруг в любое время.
Мне просто кажется, что этот тип данных - это не данные, которые вы хотите загрязнять своим статусом приложения... или даже хотите даже подумать. Похоже, данные, которые вы должны иметь возможность контролировать на уровне родительского компонента, и хотите либо сохранить, либо отказаться, не используя подход display:none
и не касаясь себя информацией о том, как он хранится.
Ответы
Ответ 1
Один из вариантов - переместить условное выражение внутри самого компонента:
Bio = React.createClass({
render: function() {
if(this.props.show) {
return <p>bio comp</p>
} else {
return null;
}
}
});
<Bio show={isBioPage} />
Является ли это "лучшей практикой" или нет, возможно, зависит от конкретной ситуации.
Ответ 2
К сожалению, трюк style={{display: 'none'}}
работает только с обычным элементом DOM, а не с компонентом React. Я должен обернуть компонент внутри div. Поэтому мне не нужно каскадировать состояние в подкомпонент.
<div className="content">
<div className={this.state.curTab == 'securities' ? 'active' : ''}>
<Securities />
</div>
<div className={this.state.curTab == 'plugins' ? 'active' : ''}>
<Plugins />
</div>
</div>
Ответ 3
Похоже, официальная документация предлагает скрывать детей с состоянием с style={{display: 'none'}}
Ответ 4
Основная проблема здесь заключается в том, что в React вам разрешено монтировать компонент только его родительскому элементу, что не всегда является желаемым поведением. Но как решить эту проблему?
Я предлагаю решение, адресованное для устранения этой проблемы. Более подробное определение проблемы, src и примеры можно найти здесь: https://github.com/fckt/react-layer-stack#rationale
Обоснование
react
/react-dom
поставляется с двумя основными предположениями/идеями:
- каждый пользовательский интерфейс является естественным. Вот почему мы имеем идею
components
, которые обертывают друг друга -
react-dom
по умолчанию монтирует (физически) дочерний компонент в родительский DOM node
Проблема в том, что иногда второе свойство не то, что вы хотите в твоем случае. Иногда вы хотите подключить свой компонент в различные физические DOM node и провести логическое соединение между родитель и ребенок одновременно.
Канонический пример - это компонент, подобный подсказке: в какой-то момент процесс разработки, вы можете обнаружить, что вам нужно добавить некоторые описание для вашего UI element
: оно будет отображаться на фиксированном уровне и должен знать свои координаты (которые заключаются в том, что UI element
мышь), и в то же время ему нужна информация о том, должен быть показан прямо сейчас или нет, его содержание и некоторый контекст из родительских компонентов. Этот пример показывает, что иногда логическая иерархия не соответствует физической иерархии DOM.
Взгляните на https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example, чтобы увидеть конкретный пример, который отвечает на ваш вопрос (посмотрите на "использование", имущество):
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>)
// ...