【问题标题】:Show/Hide ReactJS components without losing their internal state?显示/隐藏 ReactJS 组件而不丢失其内部状态?
【发布时间】:2015-02-27 16:01:00
【问题描述】:

我一直通过不渲染来隐藏/显示反应组件,例如:

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>
  );
}

但只是说&lt;Bio/&gt; 组件有很多内部状态。每次我重新创建组件时,它都会丢失其内部状态,并重置为原始状态。

我当然知道我可以将数据存储在某个地方,并通过 props 传入或只是全局访问它,但是这些数据并不需要存在于组件之外。我也可以使用 CSS (display:none) 隐藏/显示组件,但我更喜欢像上面那样隐藏/显示它们。

这里的最佳做法是什么?

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

忽略 React,并假设您只是使用一个桌面应用程序,该应用程序具有一个配置对话框,其中包含一个名为 A 的选项卡组件,该组件有 2 个选项卡,分别命名为 1 和 2。

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

按照以下答案之一的建议内部化状态,但仅适用于组件及其直接子级。如果您将组件任意嵌套在其他组件中,例如 Tabs in Tabs in Tabs,那么它们保持内部状态的唯一方法是将其外部化,或者使用 display:none 方法,该方法实际上将所有子组件保留在周围在任何时候。

在我看来,这种类型的数据并不是你想要弄脏你的应用程序状态的数据......或者甚至不想考虑。看起来您应该能够在父组件级别控制数据,并选择保留或丢弃,而无需使用 display:none 方法,也无需担心有关其存储方式的详细信息。

【问题讨论】:

  • 我明白这个问题已回答。但是您所做的不是路由问题。我们是否需要通过隐藏和显示来处理。作为最佳实践,可以将状态移动到不同的对象吗?
  • 抱歉,我之前没有看到这个 - 在这个例子中它可能看起来像一个路由问题,但这可能发生在层次结构中的任何级别......在页面深处,然后届时也会出现同样的担忧。
  • 请注意,从 DOM 中删除元素通常会导致状态丢失(例如 jQuery 数据、),因此 React 的行为并没有失控。 React 本质上为我们提供了隐藏/显示或删除/重新创建子组件的选项。父母应该知道哪个选项适合其用例(例如,选项卡需要选项 2)。我希望孩子自己不必关心父母使用哪个选项,但如果它取决于生命周期事件,它就会这样做。

标签: javascript facebook reactjs


【解决方案1】:

一种选择是将条件移动到组件本身内部:

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

<Bio show={isBioPage} />

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

【讨论】:

  • 但是如果这个组件被深深地嵌入到另一个组件中,那么如果顶层组件对它的子组件做同样的事情,它就会摆脱你的组件,并且问题会继续存在。
  • 嗯,是的。这是有道理的,会发生什么?我不确定你的建议是一个实际问题。
  • 感谢您的反馈...我已对问题本身添加了额外的编辑,以进一步描述我认为的问题!
【解决方案2】:

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

【讨论】:

  • 即使我接受了这个作为答案,这意味着对于这个组件和任何父组件都必须知道不要盲目地重新创建它的子组件,它必须隐藏/显示它们。这似乎打破了将组件作为黑盒并将其包装起来的概念。所以你的整个应用程序必须隐藏/显示一些东西来保持子组件的内部状态?我觉得不对。
  • @Brad Parks - 在您的代码中,父组件直接删除/添加子组件。此示例使用 css 隐藏/显示子组件。我看不出语义上的区别。 Colin Ramsay 有更好的解决方案,因为子组件可以控制它自己的显示。
【解决方案3】:

不幸的是,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 带有 2 个基本假设/想法:

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

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

    典型的例子是类似工具提示的组件:在某些时候 开发过程中你会发现需要添加一些 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>)
    // ...
    

    【讨论】:

      猜你喜欢
      • 2021-12-31
      • 2017-01-02
      • 2014-03-30
      • 2015-07-06
      • 1970-01-01
      • 2022-10-25
      • 1970-01-01
      • 2020-04-14
      • 1970-01-01
      相关资源
      最近更新 更多