显示/隐藏ReactJS组件而不会丢失其内部状态?

时间:2014-12-31 15:42:38

标签: javascript facebook 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)隐藏/显示组件,但我更喜欢隐藏/显示它们。

这里的最佳做法是什么?

编辑:也许更好地说明问题的方法是使用示例:

忽略React,并假设您刚刚使用的桌面应用程序具有名为A的Tab组件的配置对话框,该组件具有2个选项卡,名为1和2.

假设标签A.1有一个电子邮件文本字段,您可以填写您的电子邮件地址。然后单击Tab A.2一秒钟,然后单击返回选项卡A.1。发生了什么?您的电子邮件地址将不再存在,它将被重置为空,因为内部状态未存储在任何地方。

内部化状态的工作原理如下面的一个答案所示,但仅适用于组件及其直接子项。如果您在其他组件中任意嵌套了组件,比如选项卡中选项卡中的选项卡,那么它们保持内部状态的唯一方法是将其外部化,或者使用实际保留所有子项的display:none方法周围的组件。

在我看来,这种类型的数据不是您想要弄乱应用程序状态的数据......甚至想要甚至不得不考虑。看起来您应该能够在父组件级别控制数据,并选择保留或丢弃,而不使用display:none方法,并且不关心自己如何存储的详细信息。

4 个答案:

答案 0 :(得分:10)

一种选择是在组件内部移动条件:

Bio = React.createClass({
    render: function() {
        if(this.props.show) {
            return <p>bio comp</p>
        } else {
            return null;
        }
    }
});

<Bio show={isBioPage} />

这是否是“最佳实践”,可能取决于具体情况。

答案 1 :(得分:5)

不幸的是,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>

答案 2 :(得分:3)

看起来official documentation suggests隐藏有状态的孩子style={{display: 'none'}}

答案 3 :(得分:2)

这里的根本问题是,在React中,您只能将组件安装到其父组件,这并不总是所需的行为。但是如何解决这个问题?

我提出解决方案,以解决此问题。更详细的问题定义,src和示例可以在这里找到:https://github.com/fckt/react-layer-stack#rationale

  

原理

     

react / react-dom附带了两个基本假设/想法:

     
      
  • 每个UI都是自然分层的。这就是为什么我们有components相互包装的想法
  •   
  • react-dom默认情况下(物理上)将子组件安装到其父DOM节点
  •   
     

问题是有时第二个属性不是你想要的   在你的情况下。有时您想要将组件安装到   不同的物理DOM节点和之间保持逻辑连接   父母和孩子在同一时间。

     

典型的例子是类似Tooltip的组件:在某些时候   开发过程你可以发现你需要添加一些   您UI element的说明:它将在固定图层中呈现   应该知道它的坐标(UI element坐标或坐标   鼠标coords),同时它需要信息是否   需要立即显示或不显示,其内容和一些背景   父组件。此示例显示有时是逻辑层次结构   与物理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>)
// ...