Показать/скрыть компоненты 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>

Ответ 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>)
// ...