React: 添加片段 API 以允许从渲染返回多个组件

创建于 2014-09-02  ·  148评论  ·  资料来源: facebook/react


维护人员的注意事项:

我们知道这是一个问题,并且我们确切地知道可以解决哪些问题。 我们也想要这个,但这是我们当前架构的一个“难题”。 表达对该功能的渴望的其他评论没有帮助。 随意订阅该问题(右侧栏中有按钮),但除非您为讨论增加价值,否则不要发表评论。 “我也是”和“+1”没有价值,评论中已经写过的用例也没有价值(例如,我们知道你不能放<tr><dd>元素带有<div> )。


考虑以下:

var ManagePost = React.createClass({

  render: function() {
    var posts = this.props.posts

    var something;
    var somethingelse;

    var row = posts.map(function(post){
      return(
        <div>
          <div className="col-md-8">
          </div>
          <div className="cold-md-4">
          </div>
        </div>
      )
    });

    return (
        {row}
    );
  }

});

如果您删除<div></div>中的map ,则会收到以下错误:_Adjacent XJS 元素必须包含在封闭标记中_

直到我重新添加周围的,相当无意义的 div,它才能编译没有问题。 我正在运行0.11.1

是否正在解决这个问题? 它再次添加了额外的内容 - IMO - 页面中无用且毫无意义的 html,虽然没有任何伤害 - 看起来很混乱且不专业。 也许我只是做错了什么,如果我是,请赐教。

最有用的评论

我认为我们可以关闭它。

React 16 Beta 1 开始支持从组件返回数组,您现在可以尝试

仍然存在一些限制(SSR 支持尚未准备好),但我们正在 #8854 中跟踪它们,并将在最终 16 版本之前修复。

感谢大家的反馈!

所有148条评论

因为当您不放置包装器时,它会变成这样:

return React.DOM.div(...)React.DOM.div(...)

这在语法上没有意义。 如果您需要可视化映射, jsx 编译器页面可能会有所帮助。

话虽如此,可以将其脱糖为[div, div] 。 这很难,有些争议,并且不会在不久的将来实施。

(我不认为它特别有争议,但它确实增加了代码复杂性,而且还没有完成。)

IIRC @syranide对此有一些评论

@程楼呵呵。

前阵子和程楼聊了几句,我现在还真不是偏偏偏偏偏偏偏偏。 我看到允许复合组件返回多个组件存在很多隐患,并且它打破了许多直观的假设,但我不知道任何(现在)明显会从中受益的良好实践用例。

最多返回一个组件的简单性意味着很容易推断您看到的内容,如果不是, <table><TableHeader /></table>实际上可以渲染任意数量的行,除了检查TableHeader之外您无法知道

我觉得能够返回多个组件只会将必要时包装组件的责任从“复合组件”转移到“呈现复合组件”的任何内容。 “呈现复合组件”的组件很少知道或应该知道是否包装复合组件,而孩子更有可能知道他们的父母。

但也许这只是开发人员责任的一个案例。 两者可能都有很好的用例,我们应该忽略不可避免的误用。

@sebmarkbage可能也有一些评论:)

我们可能永远不会隐式地允许这种语法。 你需要一个像

<>
  <div className="col-md-8">
  </div>
  <div className="cold-md-4">
  </div>
</>

要么

[
  <div className="col-md-8">
  </div>,
  <div className="cold-md-4">
  </div>
]

然而,即使这样也行不通。 通常,这可能是最好的。 当子元素可能扩展到多个元素时,使用组件可能会造成混淆。

但是,真的,我们现在不支持这个的唯一原因是因为它很难实现。 希望我们能够在未来的某个时候支持它,但可能不是短期的。 对不起。 :/

这会不会影响诸如 jquery 或其他针对特定元素的库之类的东西,所以如果您执行类似$('#id-name').children()之类的操作,请执行以下操作:

<div id="id-name">
  <div>
    <div class="class-name">
    </div>
  </div>
</div>

在这种情况下,将选择<div><div class="class-name"> 。 (如果我理解正确的话)

它也以与@AdamKyle之前发布的相同方式影响 css 选择器。

关于这个问题的任何更新?

花了几分钟了解为什么我的组件不起作用。 我觉得应该在某个地方有一个通知,也许我错过了? 也许尝试显然是错误的:

var Optimistic = React.createClass({
  render: function() {
    return ( 
      <h1>{this.props.name} loves React</h1>
      <p>React doesn’t. Idea: sprinkle some divs here and there.</p>
    );
  }
});

React.render(
  <Optimistic name="Peter" />,
  document.getElementById('myContainer')
);

@gabssnake你应该得到一个 JSX 编译错误,错误“相邻的 XJS 元素必须包含在封闭标记中”; 您没有看到错误还是解释不清楚?

谢谢你的回答@spicyj。 好吧,我的意思是 React 文档中的一个通知。 是的,控制台确实显示了一个错误,但一开始我觉得需要换行没有意义。 这就是我搜索并到达这里的原因。

我也有过这种痛苦……事实上,对我的设计师来说尤其痛苦。 如果一个组件可以输出一个节点(因此是节点列表或片段)而不是一个元素,那就太好了。

只是说..我不提倡从组件中返回多个孩子_但是_我很乐意在我从render中提取的render*方法中做到这一点:

  render: function () {
    return (
      <div className={this.getClassName()}
           style={{
             color: this.props.color,
             backgroundColor: this.props.backgroundColor
           }}>
        {condition ?
          this.renderSomething() :
          this.renderOtherThing()
        }
      </div>
    );
  },

  renderSomething() {
    return (
      <>
        <div className='AboutSection-header'>
          <h1>{this.props.title}</h1>
          {this.props.subtitle &&
            <h4>{this.props.subtitle}</h4>
          }
        </div>,

        {hasChildren &&
          <div className='AboutSection-extra'>
            {this.props.children}
          </div>
        }
      </>
    );
  }

但我可能应该闭嘴并使用key s。

@gaearon虽然你已经可以做到了,你现在只需要返回一个数组(虽然有点麻烦)......但你可以解决这个问题,我已经破解了我自己的<Frag>组件它被转换为一个数组(重载React.render )......如果你想避免黑客攻击,我假设你也可以做return <NoopComp>...</NoopComp>.props.children

编辑:我的错,我重载React.createElement而不是React.render

数组的问题是他们绊倒了我们的设计师。 需要逗号,显式键。

@gaearon是的,您现在可以使用我的两种解决方法中的任何一种来避免逗号(如果您认为其中任何一种都可以接受)...但是您对显式键的含义是什么?

如果我使用数组语法,我需要在每个元素上指定key 。 并不是说这很难做到,而是感觉很尴尬,因为我知道它们永远不会改变。

@gaearon啊,是的,我现在选择在精神上忽略该警告:),如果你真的想避免它,你可以做<MyComp children={this.renderWhatever()} />来避免它(编辑:虽然你显然不能使用它如果你有相邻的孩子,你可以使用一些扁平化的助手......但是是的)。

我遇到的另一个使用 UI 工具包的案例。 当您将孩子放置在固定的可滚动容器中时,如下所示:

return (
  <div style={{
    position: fixed
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    overflow-y: scroll;
  }}>
    {this.props.children}
  </div>
);

现在必须将子类作为数组传递,但如果传递的是复合类,它还必须实现这些样式以避免破坏它。 只是增加了一层复杂性。 但我完全可以理解做出改变是多么复杂。 只是把我的帽子扔进去寻求支持。

我在此处详细描述了另一个用例 #3415。 但是我现在可以解决它,并且理解它很难实现并且很少见。

我对反应内部一无所知(还),但我只是提出一个想法,你可以如何在 DOM 中标记这样的虚拟父元素/片段:评论。 例如

<A>
    <B></B>
    <Fragment>
        <C></C>
        <D></D>
    </Fragment>
    <E></E>
</A>

将呈现为

<a>
    <b></b>
    <!--<fragment data-reactid="">-->
        <c></c>
        <d></d>
    <!--</fragment>-->
    <e></e>
</a>

这意味着 c 和 d 将被视为某物的孩子。 (包括嵌套的 reactid 与 b 和 e 不冲突)。 我已经看到其他框架“滥用”这些语义工作的注释。

@Prinzhorn我认为您可能会将 DOM 输出与 React 的 JSX 混淆。

如果有用: @Prinzhorn建议使用 HTML 注释将“片段组件”映射到 DOM,这与 Knockout 使用的方法相同。 Knockout 将这些称为“虚拟元素”。

<!-- ko component: "message-editor" -->
<!-- /ko -->

(来自淘汰赛文档

此外,另一个用例 - 使用 flexbox 时额外的包装元素可能会出现问题。

@aldendaniels绝对同意,我已经遇到了 flexbox 问题。

我使用带有依赖下拉菜单的 flexbox 遇到了这个问题。 A 将呈现和管理第一个下拉列表,然后是 B 或 C 或 D,具体取决于 A 的下拉列表的值,每个下拉列表将呈现适当数量的下拉列表,例如

<A>
 <Drop />
 <C><Drop /><Drop /></C>
</A>

A、B、C 和 D 是无状态的,所以我将它们从class B {render(){ this.props }}更改为function B(props){ return [...]; }

在这种情况下,渲染多个孩子对父母来说并不重要,它只是为了处理我的 CSS。 如果不需要重用这些东西(另一个组件需要 B、C 和 D),我会做一些不同的事情。


作为替代方案,也许是一种从父母那里解开它的方法? 我对它的外观没有任何具体的想法。

我今天遇到了一个场景,我认为这是此功能的一个很好的用例:渲染多个<script>元素的组件,它可以渲染到页面的<head>元素中。 那里的任何包装器元素都会很糟糕。

我的场景是希望有一个组件负责呈现页面上所需的运行时代码的<script>标记以及另一个携带本地化字符串的<script>标记以供使用运行时代码。 例如:

<html>
    <head>
        <script language="runtime.resources.en-us.js"></script>
        <script language="runtime.js"></script>
    </head>
    <body>
    ...
    </body>
</html>

在这种情况下,我希望将代码编写为:

var RuntimeScripts = require('./Runtime')
...
return (
    <html>
        <head>
            <RuntimeScripts language="en-us" />
        </head>
    </html>
)
...

我还遇到了一些 flexbox 问题。 没有什么是 CSS 无法解决的,但 flexbox 的“优点”之一是你需要更少的“包装”元素来让你的布局工作,但是在使用 React 时你仍然会在任何地方都有包装元素,因为您总是将返回的任何内容包装在 div/div 或类似文件中,除非拥有一个容器是有意义的。

对于此处介绍的所有用例,我很确定您可以将<BunchOfComponents />替换为{getBunchOfComponents()}并且视觉输出将是相同的,而无需介绍与使用组件相关的实际和技术问题片段作为根。

@syranide ,但每次其中一个组件发生更改时,其所有兄弟姐妹都需要重新计算...

此外,如果您使用普通的 coffeescript 很容易返回一个数组,所以请将该功能与 jsx 表示分离。
IOW 如果处理返回的元素数组很容易,请不要等待 jsx 赶上。

@syranide ,但每次其中一个组件发生更改时,其所有兄弟姐妹都需要重新计算...

@wmertens是的,但是很多时候您还是会遇到这种情况,因为父级出于其他原因需要重新渲染,或者仅仅是因为您无论如何都通过道具接收数据。 但是,是的,这就是区别,但这并不意味着这种方法是正确的,它是一种优化,有很多方法可以实现它们。

此外,如果您使用普通的 coffeescript 很容易返回一个数组,所以请将该功能与 jsx 表示分离。

这无关紧要,这不是 JSX 的问题。 一个大问题是你失去了一个组件=一个元素/节点的技术、实用和直观假设。 我不能代表开发人员说话,但我不会心甘情愿地放弃,这是一个非常有用的假设。 我敢肯定,如果优化是人们想要这个的唯一原因,那么可以设计出同样好的或更好的优化。

@syranide最大的问题是你不能总是使用包装
html 中的元素,例如表格、列表、flexbox、head...
这会导致丑陋的代码。

我会对只呈现的虚拟包装器元素感到非常满意
评论,如前所述。

2015 年 5 月 29 日星期五下午 3:56 Andreas Svensson [email protected]
写道:

@syranide https://github.com/syranide但每次其中一个
components 改变了它所有的兄弟姐妹需要重新计算......

@wmertens https://github.com/wmertens是的,但很多时候你会
无论如何都要这样做,因为父母需要为其他人重新渲染
原因,或者仅仅是因为您无论如何都通过道具接收数据。 但
是的,这就是区别,但这并不意味着这种方法是正确的,
这是一种优化,有很多方法可以实现它们。

此外,如果您使用普通的 coffeescript 很容易返回一个数组,所以请
将功能与 jsx 表示分离。

这无关紧要,这不是 JSX 的问题。 一个大问题是
你失去了 _one 的技术、实用和直观的假设
组件=一个元素/节点_。 我不能代表开发人员说话,但我不会
心甘情愿地放弃,这是一个非常有用的假设。 我确定
如果可以设计出同样好的或更好的优化
优化是人们想要这个的唯一原因。


直接回复此邮件或在 GitHub 上查看
https://github.com/facebook/react/issues/2127#issuecomment -106810565。

fwiw,将“片段”组件破解到 React 中相对容易,React 将其视为其子元素的数组。 它将自动生成密钥,但由于它发生在组件的初始验证之后,它不会抛出通常的“没有提供密钥”错误。

话虽如此,该 hack 仅解决了@gaearon上面所说的问题——不必处理丑陋的数组语法/在 JSX 中设置任意键——而不是在组件的渲染方法的根节点返回多个节点的问题。

我对组件需要返回一个“元素/节点”的想法有疑问。 对我来说,以下 JSX 结构似乎完全合理:

<Main>
  <Foo />
  <Fragment>
    <Bar />
    <Baz />
  </Fragment>
</Main>

最终成为 DOM:

<div>
  <div>Foo</div>
  <div>Bar</div>
  <div>Baz</div>
</div>

我不认为这是违反最小意外原则的,因为组件已经使用生命周期钩子对 DOM 做了各种“令人惊讶的事情”(看看常见的虫洞模式)。 人们普遍认为,组件不一定会导致创建单个元素,这很好,因为必须做出一些妥协才能与 DOM 一起使用。

这也与“优化”无关,甚至与不喜欢数组语法有关。 正如许多用户所提到的,_wrapper 元素会严​​重破坏样式和布局_。 表格是最明显的,但 Flexbox 也是一个主要问题。 我已经有了 CSS,它只是将 flex 规则重新应用到仅因 React 而存在的包装元素,而且它非常难看。

对于此处介绍的所有用例,我很确定您可以替换使用 {getBunchOfComponents()} 并且视觉输出将是相同的,而没有引入与以片段为根的组件相关的实际和技术问题。

这要求开发人员在制作隔离的、可重用的组件方面做出妥协——如果他们决定在其他地方重用他们的一堆组件,天堂会帮助他们——因为 React 中存在底层实现问题。 我认为这不应该被接受。

@thomasboyt

编辑:我的错误,我将你的一些论点与上面的表格讨论混为一谈,我基本上同意你所说的我的想法。 但是组件不透明仍然存在问题,因此作为有用的透明包装器的意图对父级变得不透明。 想象一下<Wrapper1><Wrapper2>...</Wrapper2></Wrapper1>Wrapper1看不到Wrapper2的孩子。 所以也许wrapMyElements(...)仍然是一个更好的解决方案(包括任何其他必要的支持功能)。

我对组件需要返回一个“元素/节点”的想法有疑问。 对我来说,以下 JSX 结构似乎完全合理:

组件不仅仅是愚蠢的包装器,它们是有目的的。 恕我直言,似乎返回多个元素会阻止一些非常有用的期望。 例如, React.render将来会得到一个同伴,它会渲染一个元素并返回节点,现在它必须生成一个节点数组。

但我认为一个非常重要的问题是可读性,恕我直言,这是 React 的最大卖点,一切都是明确的。

<table>
  <tr>
    <td />
    <td />
    <td />
  </tr>
  <tr>
    <Columns1 />
    <Columns2 />
  </tr>
</table>

看着没有意义,第三个细胞是从哪里来的? 也许它实际上是错误的,它正在渲染 2 或 4 个单元格,谁知道呢,也许它实际上是动态的并且取决于道具或外部状态? 这个问题有很多变体,只有在考虑其他可能有明确期望的非 HTMLDOM 前端时才会变得更加复杂。 要考虑的另一件事是元素是不透明的,因此如果将<tr />替换为<MyMagicalTr />则它无法与单个单元格交互,甚至无法推断出有多少个单元格,所以即使<MyMagicalTr />可能只接受<MyMagicalTd /> ,但不能保证它实际上可以与它们交互。

这要求开发人员在制作隔离的、可重用的组件方面做出妥协——如果他们决定在其他地方重用他们的一堆组件,天堂会帮助他们——因为 React 中存在底层实现问题。 我认为这不应该被接受。

“这需要开发人员在制作隔离时做出妥协……”,但如果您问我,这正是问题所在,如果一个组件可以返回多个元素,则它不再是隔离的,而是被替换的,组件正在泄漏到父级中。

锤子,钉子。 它是一个潜在的实现问题,与这是否应该真正完成是一个单独的问题。 这不是我的决定,但我看不出一个罕见的用例是如何令人信服的论点,而不考虑它带来的权衡或其他替代解决方案。

恕我直言,我没有看到{getBunchOfComponents()}的问题,它很明确,它允许我们保持有用的期望。 如果性能是一个问题,那么React.createSmartFragment() (或 w/e)来救援,这是一种透明的数组/类似对象的类型,但它可以独立于其父项进行更新。

同样,React 开发人员是权威(不是我),但考虑到各种副作用,我认为这里没有令人信服的论据。 我什至不确定我是否完全同意所提出的解决方案是一个好的模式,即使它得到支持。

编辑:为了澄清,也许组件将来可能能够返回多个元素,因为还有其他明显有益的用例,特别是在传递孩子的情况下(比如你展示的@thomasboyt),可读性得到保持。

我想我需要多喝点咖啡才能回应这次谈话的哲学方面(感谢@syranide 的非常好的观点),但在实施方面,我昨晚开始四处寻找,看看如何可行的这个范围的变化是,导致这个峰值: https://github.com/facebook/react/compare/master...thomasboyt :fragment

并在这里抛出了一个小演示: http ://www.thomasboyt.com/react-fragment-demo/

关于实现方面的一些观察:

  • 毫不奇怪,改造一个期望“1 个组件 = 1 个节点”以支持更多节点的系统是非常棘手的;)
  • 我最初想尝试在 DOM 操作端跟踪片段,以便ReactMultiChild生成的突变指令可以保持不变并像对待任何其他节点一样对待片段。 不过,我想不出一种将节点计数/哪些节点是片段的状态添加到 DOM 状态跟踪中的好方法。 @Prinzhorn指出的评论围栏可能会起作用,但考虑到相对成本,我对任何需要 DOM 查找的东西都持谨慎态度。
  • 放弃了这个想法,我向ReactMultiChild组件的所有子组件添加了一个_nodeCount字段,以便它可以跟踪片段实际包含的根节点的数量。

问题是,虽然这很容易在初始渲染上通过仅计算片段的子节点来完成,但在后续突变上更新片段的节点计数似乎更棘手。 这还没有在我的分支上完成(参见 https://github.com/thomasboyt/react/issues/2)。

  • 许多 DOM 操作依赖于访问元素的父节点,通过内部节点 ID 查找,以追加/移动/删除元素(请参阅 https://github.com/thomasboyt/react/issues/3)。 由于ReactMultiChild的 updateComponent 循环负责传递此 ID,因此可以将其更改为查找最近的具有 DOM 节点的父节点,但这听起来很昂贵。 或者,可能有一个片段键的内部注册表到它们的“真实节点”键。

我仍然不相信需要片段来维护其根节点的数量是做到这一点的最佳方法(尽管它至少让我进入了那个演示),而且这一切都在深夜很快地被破解了,所以如果其他人有实施建议,请随时加入:>

@thomasboyt IIRC 主要实现障碍来自 React 通过mountIndex引用子节点,当一个“节点”突然变成任意数量的节点时,这不起作用,这可能在不调用父节点的情况下发生,也可能发生几个组件深(包装)。 如果我没记错的话,只要数字永远不会改变,让 React 支持多个根元素是非常简单的。

所以我不认为仅仅让它在 React 中工作会特别困难,但是一个真正合适的解决方案更有问题,可能应该包括删除mountIndex

@syranide对; 我正在研究的解决方案实际上引入了一个新的nodeIndex ,它应该是节点的“实际偏移量”(这提醒我我需要返回并删除mountIndex ,因为我认为它现在在我的分支中未使用)。

但是,正如您所注意到的,如果根元素的数量发生变化,这是有问题的,因为只要前一个兄弟组件的节点数发生变化,就需要更新组件的nodeIndex 。 仍然需要为此找到解决方案。

我也遇到过 flexbox 问题。 @syranide您能否详细说明您提出的“getBunchOfComponents”解决方案? 作为 React 的新手,很难完全理解在哪里定义这个函数/如何应用它。

@landabaso

function getBunchOfComponents(...) {
  return [<ColumnA key="a" />, <ColumnB key="b" />];
}

嘿,

我还没有阅读整个线程,但这里有一个可能需要此功能的渲染优化用例:

http://stackoverflow.com/questions/30976722/react-performance-rendering-big-list-with-purerendermixin

如果这个特性发布了,ReactCSSTransitionGroup 就不再需要包装节点了,对吧?

@slorber是的,这可能是真的。

每天都需要这个功能。

如果你有很多小组件(即设计非常模块化),你最终不得不将各种不应该的东西包装在 div 中。 我可能会混淆,但我认为这与这个问题有关。

对于<div> ,您可以将它们包装在<div>中,但是对于表行<tr>元素,这并不容易。 您可以将<tr>包装在<tbody>中,但可能不希望有多层<tbody>包装多层<tr>

我调用的场景是试图让一个组件提供<link><script>元素,而不必完全成为<head>渲染器。

我在这个问题的顶部添加了一个注释。 请在评论之前阅读它。 https://github.com/facebook/react/issues/2127#issue -41668009

凹凸...我在服务器端非常需要它。 由于<head>部分,在无法呈现片段的情况下呈现完整的网页(不包括 doctype)非常复杂。 我目前正在通过 mixins 和最终渲染中的一些逻辑来解决这个问题,但如果支持渲染多个组件,它会简单得多。

@impinball您可以尝试基于react-side-effect编写类似于react-document-title的内容来解决这些问题。 我能够对元标记、标题、标题和偶尔的重定向做同样的事情

我也遇到了这个问题,请问有什么解决办法吗? 我无法按照建议让{getBunchOfComponents()}工作。

除了已经提到的那些。

@jonchay您可以创建一个仅呈现其子级的组件。

function statelessWrapper(props) {
   return props.children;
}

然后使用它:

render() {
   return (  
      <statelessWrapper>
         {renderABunchOfComponents()}
      </statelessWrapper>
    );
}

@whatknight除非return renderABunchOfComponents();已经起作用,否则这将不起作用。

  render () {
    let user = this.state.user
    let profile = user.get('data')
    let view = null

    if (user.get('status') === 'fetched') {
      view = (
        <h1>{profile.get('login')}</h1>
        <img src={profile.get('avatar_url')} />
        <dl>
          <dt>email</dt>
          <dd>{profile.get('email')}</dd>
        </dl>
      )
    } else if (user.get('status') === 'fetching') {
      view = <h1>fetching</h1>
    } else if (user.get('status') === 'error') {
      view = <h1>{profile.message}</h1>
    }

    return (
      <div className={className}>
        {view}
      </div>
    )
  }

至少应该有一种方法可以在进行插值和“组装”时返回多个片段。 上面的例子抱怨 img 和 h1 是 adiacent 但无论如何它们最终都会在主包装器中。 这是我希望我能摆脱的一个包装元素。

@kilianc在这种情况下,您可以简单地写

      view = [
        <h1 key={0}>{profile.get('login')}</h1>,
        <img key={1} src={profile.get('avatar_url')} />,
        <dl key={2}>
          <dt>email</dt>
          <dd>{profile.get('email')}</dd>
        </dl>,
      ]

你使用它的方式,如果这个问题得到解决,它不会有什么不同。

由于已经说明的原因,我需要此功能,因此尝试在https://github.com/mwiencek/react/tree/frag-component实现一个<frag></frag>容器

实现不是很漂亮,但如果它适用于人们,我可以提交 PR 并让 React 开发人员将其拆散。

@mwiencek如果片段中的子项数量在更新中发生变化(_nestedChildCount 仅在 mountComponent 中设置),您的实现似乎不起作用? 让这一切正常运行有点棘手。 看起来你有一个好的开始。 实际上,我最近一直在考虑这个问题,我可能已经找到了一种可靠的方法来实现这一点。 如果我发现成功,我会报告。

@spicyj是的,你是对的,我需要调查一下......

不过,我们可能很快就会看到一个正确的实现,这让我们非常兴奋。 :) 如果它们有任何用处,请随意从该分支复制测试。

@spicyj使用createFragment并让 JSX 转换成这样的方法不是吗? 还是我们真的希望片段成为元素?

要构建和扩展@syranide的最后一条评论,如果 render 允许将数组作为返回值,似乎不需要额外的“片段 API”。 JSX 可以将多个根元素转换为一个数组,这也适用于任何其他函数的返回值。 因此,与其引入需要文档和学习的额外 API 表面,不如消除 React 的限制之一。

这至少会影响babel-plugin-transform-react-jsx (实现)和babel-plugin-syntax-jsx (删除相邻根元素的解析错误)。 虽然更改前者似乎相当安全,但我不知道后者的范围/用途以及提议的更改对其他项目的影响。

这仍然没有涵盖具有多个元素的条件的用例。 我不认为“使用数组并手动将任意key={...}添加到每个元素”是适当的长期解决方案。

同意@dantman

是的,好点。 应通过转换内置自动密钥生成。 使用数组索引作为键就足够了,因为项目没有改变。

至于条件,这也可以内置到转换中,或者您可以使用JSX-Control-Statements 。 以这种方式在那里实现它,因此有了这个想法。

为了正确处理更新,我认为@spicyj为#5753 考虑的解决方案也可能适用于片段(将内容包装在<!-- react-frag: 1 --><!-- /react-frag: 1 -->之类的东西中)。 是的,评论有点难看,但它比我试图用_nestedChildCount做的更可靠。 这种方法现在在https://github.com/mwiencek/react/tree/frag-component使用

到目前为止,我还没有在线程中看到这一点,但我认为解决这个问题也可以提高可组合性。 例如,假设您有一个网格,您希望其中的单元格按特定顺序淡化。 理想情况下,这里有两个组件在起作用:一个处理布局,另一个处理动画。 你会有这样的 API:

<GridLayout
  columns = { 3 }
>
  <FadeAnimator
    springConfig = { springConfig }
  >
    { ...cells }
  </FadeAnimator>
</GridLayout>

这将使您能够切换到不同的布局或不同的动画,而无需了解另一个的实现细节。 GridLayout会期望收到一个孩子的列表。 FadeAnimator将拦截该列表,注入适当的样式和/或事件侦听器,并返回新列表以供GridLayout使用。 FadeAnimator没有理由关心布局网格,除了 React 元素不能从渲染返回数组。 此外,没有简单的方法可以用砖石布局替换网格,因为FadeAnimator作为其子项的容器。

在目前的限制下,我想你可以做这样的事情:

<FadeAnimator
  wrapper = {
    <GridLayout
      columns = { 3 }
    />
  }
  springConfig = { springConfig }
>
  { ...cells }
</FadeAnimator>

// FadeAnimator
render() {
  return React.cloneElement(
    props.wrapper,
    null,
    props.children
  );
}

但这会使代码变得不那么清晰、更复杂、更难编写。

添加片段 API 以允许从渲染返回多个组件!
添加片段 API 以允许从渲染返回多个组件!
添加片段 API 以允许从渲染返回多个组件!
添加片段 API 以允许从渲染返回多个组件!
添加片段 API 以允许从渲染返回多个组件!
添加片段 API 以允许从渲染返回多个组件!
添加片段 API 以允许从渲染返回多个组件!
添加片段 API 以允许从渲染返回多个组件!

@texttechne建议更好。 react 应该在 render 中处理多个根元素,而不是引入额外的 API。

我认为在渲染中处理多个根元素会很困难。
这意味着那里: https ://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js#L1089

您将拥有一个元素数组而不是一个元素。
因此,据我了解,您现在必须实例化多个 React 元素: https ://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js#

然后,挂载多个实例化的 React 元素: https ://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js#L471

并协调以良好顺序生成的所有标记。

我认为它具有可能不想绕过和解过程的缺点。
我们想要片段作为元素还是片段作为围绕转换的糖语法?

我认为片段作为元素是可以的,我们只需要创建一种类似于文本节点或空节点的新型内部节点,对吗? 虽然我不知道我们将如何管理它们。

例如,当其中一个根被卸载时,您如何处理? 您如何正确处理更新?
或者,您如何在 DevTools 中管理多个根? (显而易见的答案:修复 DevTools...)

我认为片段是一个复合组件。 具体区别在哪里?
如果我们最终为了实现片段而重复代码,我们最好实现糖语法以保持 React 内部“原始”?

我想知道,我一直在围绕 Subtree 问题 (renderSubtreeIntoContainer) 使用 React 内部,我觉得它有点相关。 当你想渲染一个新的子树时,你实际上必须渲染一个新的根。 所以,如果我们在树级别支持多个根,我们是否每次都渲染新的根:

<p>Hi</p>
<p>There</p>

将导致两个“渲染到新的根”调用。

如果我们使用包装器,而不是一个调用,对吧? 性能呢? 简单? 老实说,我的感觉是:我们不应该接触 React 内部来处理这种情况。 相反,我们可以用 JSX 实现这一壮举吗? 我们可以改进 JSX 语法吗?

(_Disclaimer_:我并不完全习惯 React 的内部结构,可能有些部分我不完全理解或不理解。对于误解,我深表歉意。)

编辑:修复/澄清事情。 此外,GitHub 奇怪地对电子邮件进行了奇怪的样式设置,所以我不得不重新格式化代码块...... :-(

嗨,这里是 Mithril 的核心贡献者/提交者。

TL;DR: Fragment 非常困难,即使 API 和内部是
简单的。

顺便说一句,我从经验中知道它_非常_难以实施。 这
Mithril 也被多次请求,但由于以下原因被拒绝
纯粹的困难。 实施它的每一次尝试都失败了
至少有三分之一的测试套件失败。

我仍在研究我计划编写的 vdom 库的详细信息,
它会把所有东西都当作一个片段,但这是你拥有的东西
从头开始(重新)编写渲染部分。 像 React 一样,
它将与 DOM 解耦,但 API 会显着不同
概念上用于渲染。

这是碎片的问题:你必须完全管理它们
在内部,或者您没有正确区分它们。 甚至
document.createContextualFragment没用。 举个例子,让我们
变换两棵树,渲染省略:

// Before
A {}
fragment {
  B[class="foo"] {}
  B[class="bar"] {}
}
C {}
D {}

// After
A {}
B[class="foo"] {}
fragment {
  C {}
}
D {}

正确的转换应该是替换B元素和C元素,其余部分保持不变。 弄清楚这一点并非易事,您必须基本上迭代片段的孩子,同时忽略它们在片段中的事实。

但是当片段消失时,你必须处理钩子语义,比如
shouldComponentUpdate (我不记得那个钩子的 React 名字了)。 所以
您仍然必须独立跟踪片段。 你区分他们
内容就好像它们是其父片段的一部分,但您仍然拥有
为了组件的缘故跟踪该片段的位置。

或者换句话说,组件不再与它们的内在联系
DOM 节点。 相反,它们链接到相应的片段。 与大多数其他 vdom 库和框架一样,React 本质上将组件与其树表示耦合,即使是预期的类型。 这是实现差异算法的最简单方法
处理组件。 当它们解耦时,您必须分开
两者的簿记。 初始化时不初始化组件
节点。 现在是两个完全独立的过程。 很难做到
最初,之后更难添加支持。

谢谢大家的话。 我们知道这很难,并且仍然在我们的待办事项清单上。 (热情地询问不会早点实现@janryWang。)

@isiahmeadows仅供参考,秘银的重写分支确实支持片段。

@spicyj欢迎您查看实现[1] [2]并测试[1] [2]如果您还没有关注它。 整个差异引擎只有大约 400 LOC,所以应该很容易理解

@isiahmeadows我认为 GitHub 吃了你的部分评论。 代码块已损坏,我看不到通过了<D />的第一个实例。

不过在电子邮件中看起来还可以。 也许你在 GitHub 中发现了一个错误?

不幸的是,当评论来自电子邮件时,GitHub 的降价处理行为会有所不同。 我编辑了评论以删除一个空行,现在它显示出来了。

我将原始电子邮件转发到support@github。 希望他们可以修复解析器。 😃

@lhorie您对片段使用数组语法吗?
你有 DocumentFragment 的 polyfill 吗?

并且使用“伪”包装元素,比如 HTML 注释不是一个选项? 我认为这就是“解决”文本节点的方式......

感谢您修复该评论@spicyj

为了解决@isiahmeadows在他的示例中提出的问题:新的 Mithril 引擎确实_不_遵循@isiahmeadows建议的语义,原因如下:

  • 实现这些语义将使差异_显着_更复杂
  • 这将使得很难推断键和与键相关的错误,因为键空间可能会从组件中流出,甚至会渗入兄弟和子组件。
  • 它会使片段生命周期变得不直观(例如,在该示例中, B.bar被删除,但片段也是如此,并创建了一个新片段来包装 C)。 这违反了生命周期“级联”的一般原则,这意味着如果删除了给定的父节点,您就不能再确定删除了子节点。 与前一点一样,这有可能导致组件的封装能力泄漏。
  • 如果假设确实遇到了与不遵守这些语义的差异引擎相关的差异问题,那么应用程序空间解决方案就像在有问题的节点周围包裹一个片段一样微不足道。

如果核心团队可以扩展顶部的注释,我会很感兴趣:_为什么_这对于当前的架构来说很难。 看到 React 和 Mithril 的渲染引擎从根本上试图解决相同的问题,并且 Mithril 现在确实支持片段到我认为可行和有用的程度,如果语义的不同方面在 React 中实现它可能更可行像秘银一样单独评估(并且可能被拒绝)。

请注意,我修正了我的评论。 我犯了一些错误,而 GitHub 在样式化电子邮件回复方面做得不好……:皱眉:

@Primajin我也想知道,但我怀疑它们会作为一个元素传递。 使片段可组合很重要(参见我上面的示例)。 但是,有时您也可能希望将它们视为一个单元。

也许, React.Children.map应该扩展片段。 如果您想遍历每个孩子(包括孩子片段的孩子),请使用Children.map 。 如果您想将片段视为不透明的盒子,请直接将 props.children 用作 {array, element}。

@lhorie不过,我没有参与重写,所以我不太熟悉它的复杂性。 我一直忙于这周和下周有三个期末考试的事实,而且我正在与某人合作,以安排我的大学需要的实习。 我也一直专注于完成 Techtonic,我_几乎_完成了 CLI(一个不应该的测试被破坏了)。

@isiahmeadows只是一个友好的提醒,要保持话题。 如果您想讨论其他话题,请随意使用秘银 gitter 房间。

@appsforartists

也许,React.Children.map 应该扩展片段

Mithril stable 在内部做了类似的事情(即展平子阵列),但由于性能原因(也由于一些历史上关于混合键控和非键控节点的 vnode 列表的问题),我正在远离该模型。 可能是需要考虑的事情。

并且使用“伪”包装元素,比如 HTML 注释不是一个选项? 我认为这就是“解决”文本节点的方式......

几个月来,我们一直在生产环境中使用https://github.com/mwiencek/react-packages 。 它使用这种注释包装方法,因此可以明确嵌套片段。

@mwiencek可以在没有自定义反应包的情况下使用您的方法吗?

@mwiencek是否需要评论包装器? 我不会期望片段中有聪明的东西,如果你将一个元素从一个片段中移到一个兄弟片段或根元素中,它可以被重新创建。

因此,如果您按顺序遵循 vdom 树,则不需要注释,对吗?

无论如何,乍一看,您的解决方案看起来正是解决此问题所需要的。 👍

不严格,但他们在这种情况下使实现更简单。

所以基本上目前不可能用 React 创建正确的描述列表<dl>

<dl>
  <dt>Def 1</dt>
  <dd>Some description</dd>
  <dt>Def 2</dt>
  <dd>Some other description</dd>
</dl>

@KaiStapel这个问题是关于从render()返回多个组件(或我猜的元素)。 只要您的render函数只返回一个根元素/组件,它就可以工作。

好的:

render() {
  return (
    <dl>
      <dt>Def 1</dt>
      <dd>Some description</dd>
      <dt>Def 2</dt>
      <dd>Some other description</dd>
    </dl>
  )
}

不好:

render() {
  return (
    <h2>my list</h2>
    <dl>
      <dt>Def 1</dt>
      <dd>Some description</dd>
      <dt>Def 2</dt>
      <dd>Some other description</dd>
    </dl>
  )
}

@GGAlanSmithee硬编码是的,但你不能这样做:

<dl>
   loop here and print out dt/dd pairs
</dl>

这是非常可悲的。 具有行跨度的表也是如此,因为您不能一次呈现两个<tr>元素:(

从顶部:

“我也是”和“+1”没有价值,评论中已经写过的用例也没有价值(例如,我们知道你不能将 <tr> 或 <dd> 元素与 <div> 放在一起) .

鉴于https://github.com/mwiencek/react-packages中有一个可行的解决方案,这是否有可能很快成为 React 的一部分? 还是我们在等待新的调和器?

鉴于https://github.com/mwiencek/react-packages中有一个可行的解决方案

您是否在实际项目中成功使用它?

@mwiencek

实现不是很漂亮,但如果它适用于人们,我可以提交 PR 并让 React 开发人员将其拆散。

当然,请发送 PR!

关于提交 PR,我从@spicyj那里听到的最后一个消息是,他们想要完成新的核心算法并为此做一个适当的解决方案,因为评论节点在 React Native 中并没有真正意义。 我一直没有关注它的状态,但我认为这些计划没有改变。 我很高兴人们在此期间发现这些软件包很有用。

新算法正在开发中并支持片段。 但是,我不希望它在未来几个月内准备好生产。 我想知道先将它添加到 React DOM 然后再添加到 React Native 是否太糟糕了? 不利的一面是它会稍微破坏生态系统(双关语!),但它可能会给我们一些时间来试验该功能。 我们今天有一个团队会议,所以如果我们有时间更好地了解,我会提出这个问题。

@gaearon我可以指出支持片段非常简单,它只是糖,我目前正在使用<frag>作为一个小小的包装器。 返回多个子组件作为组件根目录真的那么重要吗?

@syranide我在 beta 环境中使用带有 frag 的自定义构建,但是我想使用官方的 React 构建。 你能提供你的<frag>包装器吗? :) 谢谢

@amertak

import React from 'react';
import createFragment from 'react-addons-create-fragment';

let nativeCreateElement = React.createElement;

React.createElement = function() {
  if (arguments[0] !== 'frag') {
    return nativeCreateElement.apply(this, arguments);
  }

  let length = arguments.length;
  if (length <= 2) {
    return null;
  }

  let children = {};
  for (let i = 2; i < length; i++) {
    children['~' + (i - 2)] = arguments[i];
  }

  return createFragment(children);
};

我们在上次团队会议上讨论了更多关于这个的内容。 共识是我们不想采用这个特定的实现。 但是,长期核心重写将支持此功能(目前尚无时间表)。

如果重写时间过长或没有成功,我们还将再次考虑将其作为今年下半年可能开展的工作之一。 不保证它会列入名单,但如果出现,我们会及时通知大家。

为了更好地了解我们的工作,请查看我们的会议记录回购! 您可以在https://github.com/reactjs/core-notes/blob/master/2016-07/july-07.md 中找到我们对此的最新讨论。

@gaearon至少有一个官方的片段语法会很有趣。

@syranide感谢您的代码,但不幸的是,我似乎无法使用它,因为我需要frag作为通过ReactDOM.render方法渲染的根应用程序组件,并且此方法不会接受片段.

无论如何,谢谢,它对于不需要frag作为应用程序根目录的其他人很有用。

@amertak是的,它只是为了启用更合理的语法来创建片段,它不添加任何新功能。

@syranide
我在想是否可以手动呈现评论并将其视为另一个组件(可能没有必要)?
内部评论被处理为#comment类型,所以也许可以调用
React.createComponent('#comment', { ... }, children) ?
只是一个想法。 小解决方法。

这里缺少的关键是能够渲染comment节点,对吗? :)

@gaearon有点遗憾,这不会很快到来,但我很欣赏这种透明度。 好好写!

更新之前的替代解决方案?
对我来说,我需要渲染Botstraap的下拉菜单

render(){
    return (
        <ButtonNotification/>
        <ul className="dropdown-menu">
            {this.state.items.map(this.addItem)}
        </ul>
    );
}
ReactDOM.render(
    React.createElement(NotificationHandler, null),
    document.getElementById("listNotification")
);

但这是不可能的,一个想法?
谢谢,

http://getbootstrap.com/components/#btn -dropdowns-single

如 Docs 所示,它清楚地包含在单个<div class="btn-group">

当然可以,但是<div class="btn-group">中包含两个元素。

render(){
    return (
        <ButtonNotification/> //ELEMENT 1
        <ul className="dropdown-menu"> //ELEMENT 2
            {this.state.items.map(this.addItem)}
        </ul>
    );
}
ReactDOM.render(
    React.createElement(NotificationHandler, null),
    document.getElementById("listNotification") //is DIV#listNotification
);
<div id="listNotification" class="btn-group"><!--wrap-->
    <a href="#">button notification</a> <!--ELEMENT1-->
    <ul> <!--ELEMENT2-->
        <li></li>
        <li></li>
    </ul>
</div>

并且不可能将两个元素包裹在单个元素中,
感谢您的时间@Primajin

只需将所有内容移至一级:

render(){
    return (
        <div className="btn-group"> //WRAP
            <ButtonNotification/> //ELEMENT 1
            <ul className="dropdown-menu"> //ELEMENT 2
                {this.state.items.map(this.addItem)}
            </ul>
        </div>
    );
}
ReactDOM.render(
    React.createElement(NotificationHandler, null),
    document.getElementById("listWrapper") //or whatever your list is called
);

好的,但我在父项中有其他项目。
这只会移动问题。

<div ...> <--!listWrapper-->
    <div class="btn-group">....</>
    <div class="btn-group">....</>
    <--!React-->
    <div class="btn-group"> //WRAP
        <ButtonNotification/> //ELEMENT 1
        <ul className="dropdown-menu"> //ELEMENT 2
            {this.state.items.map(this.addItem)}
        </ul>
    </div>
    <--!React-->
    <div class="btn-group">....</>
</div>

而在这种情况下,React 将替换所有包含的内容。
是否可以在不替换其他元素的情况下进行“添加”?

谢谢,

@rifton007这不是它的工作原理。 您可以将多个元素/组件作为兄弟姐妹,包装在一个容器中。 限制是一个组件不能return多个元素。 您刚刚发布的代码将起作用。

话虽如此,如果您确实提出了一个不起作用的示例,请阅读整个线程并考虑您是要添加 _new_ 示例还是只是重复已经确认的相同问题。 这是一个已知的限制,并且此线程包含对限制的详细信息和推理的扩展讨论。 Dan 还表示,他们打算最终解决此问题。 通过指出另一个已确认问题的示例,您试图完成什么?

抱歉,我只是想知道在此期间是否有人会有替代解决方案。
如有必要,您可以删除我的帖子。

我使用 Framework7 和 React 创建应用程序,但是 framework7 html 格式是固定的,

<div class="page"><div class="navbar"></div><div class="searchbar"></div><div class="page-content"></div><div class="toolbar"></div></div>

我无法将第一级子元素(导航栏、搜索栏)包装在没有“页面”类的其他 div 中

我有一个返回行列表的组件

要在表格中使用并且我不能将它们包装在额外的 HTML 标记中(HTML5 标准不允许 - 我知道 tbody 但我可能有多个子组件返回的多行,这些子组件可能需要组合成一个 tbody )。 @Prinzhorn提到的技术 - 将这些孩子包装在 HTML 注释中 - 是否实际上已被任何人实施? 我试图实现一个只呈现 HTML 注释的组件,但它似乎不起作用。

仅供参考,我们正在进行的重写(#6170)已经支持片段。 您可以在 #7925 和http://isfiberreadyyet.com 上跟踪我们的进度。

<table>元素不能渲染某些元素的事实足以说明添加此功能以兼容 Web API。

编辑:啊,碎片! 期待他们。

@trusktr

正如该线程的第一篇文章中所述:

维护人员的注意事项:

我们知道这是一个问题,并且我们确切地知道可以解决哪些问题。 我们也想要这个,但这是我们当前架构的一个难题。 表达对该功能的渴望的其他评论没有帮助。

😉

天哪,我得停止这样做。

我对此表示赞成,但想分享一个有效的用例。 假设我有 3 个按钮,我想要一个 CSS 液体布局(左、中、右)。 左侧按钮始终可见,但其他两个按钮是有条件可见的。 在 React(特别是 React Native)中,我使用它的 flexbox 并将其渲染为:

[ Left ] { renderCenterAndRightButtons(this.state.someFlag) }

中间和右侧按钮的位置不正确,因为它们被包裹在一个单一的视图中。

是的,我可以将中心按钮和右按钮拆分为它们自己的方法,但这会引入大量冗余代码,因为它们共享很多行为。

[ Left ] { renderCenterButton(this.state.someFlag) } { renderRightButton(this.state.someFlag) }

就像上面所说的那样,我们已经知道这个功能的用例,并且正在努力支持它。 (上面已经有六个人提出了 flexbox 问题。)因为看起来更多的讨论不会有成效,所以我锁定了这个。 当我们有关于新实施的消息时,我们会更新。

我认为我们可以关闭它。

React 16 Beta 1 开始支持从组件返回数组,您现在可以尝试

仍然存在一些限制(SSR 支持尚未准备好),但我们正在 #8854 中跟踪它们,并将在最终 16 版本之前修复。

感谢大家的反馈!

谢谢丹

🍾🍾🍾

🦏

@gaearon很棒的改进! 是否支持纯文本节点?

是的,它也支持返回字符串。

请问这应该如何工作?

我希望它能让我们渲染 2 个相邻的 XJS 元素,但如果我这样做return ["foo", "bar"] (更有用的东西;),我会得到预期的bundle.js:66656 Warning: Each child in an array or iterator should have a unique "key" prop.

那么这个特性是不是一种不用无关元素包围真实列表的方法呢?

那么这个特性是不是一种不用无关元素包围真实列表的方法呢?

是的,一种从一个渲染中提供多个元素的方法。 (有关更多信息,请参阅最初的问题帖子。)

如果已知所有数组项都是字符串,只需将它们连接起来并返回一个字符串。 如果没有,你可以吃掉这个警告,或者想办法将它们包装在元素中,这样它们就可以被键入以改进 DOM 协调,就像你在 JSX 中的另一个元素中包含一个字符串数组一样。

@diligiant返回['foo', 'bar']与此问题无关。 你永远不能也仍然不能这样做return <div>{['foo','bar']}</div>数组中的每个孩子都应该有一个“key”道具,无论它是在像<div>这样的内部 jsx 标记中还是被返回。 你现在可以做的是:

return [<div key='1'>foo</div>, <span key='2'>bar</span>];

它消除了反应的一个很大的限制。

顺便说一句,返回['foo', 'bar']会给您一个警告而不是错误,并且要删除该警告,您可以轻松地加入这些字符串,或者如果它们是 jsx 标签而不是字符串,您可以向它们添加一个 key prop。 所以返回数组没有限制。

@JesperTreetop谢谢。
@sassanh ,从你的例子来看,我似乎太“害羞”而不能“只是”使用键来避免周围(语义上)无用的<div> 。 你的意思是我应该继续并且没有真正的性能损失? 这确实是一个真正的改进!

@diligiant我怀疑它会带来任何性能损失,但您不需要周围无用的div ,除非它是周围的字符串,如果它是周围的字符串,您可以完全避免使用数组并加入字符串。 如果它不是字符串,您可以将密钥添加到组件。 例如,如果你有两个组件FooBar你可以返回[<Foo key='foo'/>, <Bar key='bar'/>]并且你不需要包围你的组件(像往常一样)和你的数组(感谢这个新版本,你需要在 16 之前包围阵列)。

@sassanh那么酷。 当然,我的用例包含多个组件。 快乐的!! ;)

实际上,当所有项目都是字符串时,我们警告丢失键对我来说似乎很奇怪。 我不希望我们这样做。 当字符串在 div 中时,这是 15 中的现有行为吗? 如果不是,那么我们应该将其修复为 16 以用于顶级字符串(因为不可能为它们提供密钥)。

@gaearon抱歉,我的示例具有误导性:我的意思是组件。

我检查并在-beta.5下,React 在渲染具有多个字符串的数组时不会发出警告。

尽管如此,我对key的要求感到困惑,因为它只是为了避免周围的组件而渲染一个数组。 我理解,这没什么,但它可能会引发大量关于 SO 的问题(但我没有什么比这更好的了……)

最后,再次感谢您。

此警告与组件数组的所有其他用法完全相同,因为它归结为协调器的完全相同的“额外工作”,因此通过设置key进行完全相同的潜在优化。 令我惊讶的是,如果组件数组根据这一点被区别对待,我想这也会引发一些 Stack Overflow 问题。 更不用说我认为首先要跟踪它会涉及一些开销。

..大多数时候我会从一个函数返回一个数组,我确实想要key警告。 似乎你不想要警告的时间是少数,React 没有办法知道不警告是否合适。

警告是必要的,因为缺少密钥会导致正确性问题,而不仅仅是性能问题。 这在许多其他问题中都有解释,询问为什么需要密钥,因此我鼓励您搜索它们并阅读这些讨论。 我同意文档可以对此更清楚,这是我们下次进行文档更改冲刺时可能会查看的内容。

从渲染直接返回的数组和 div 内的数组之间没有概念上的区别。 因此,没有理由在一种情况下会出现关键警告,而在另一种情况下不会出现。 它们需要以相同的方式工作,因为当密钥丢失时,两者都会受到相同问题的影响。

也就是说,我们理解为静态内容指定键很烦人。 就像您在编写一个静态已知子项(因此永远不会重新排序)的 JSX 结构时不指定键一样,如果有一种方法可以使用数组来做到这一点,那就太好了。

将来我们可能会通过在 JSX 中添加对片段的显式支持来解决这个问题,语法如下:

return (
  <>
    <div>child 1</div>
    <div>child 2</div>
  </>
);

它可以生成一个数组,但隐式地为子代分配数字索引,因为在这种情况下,我们知道它们永远无法重新排序。 JSX 子表达式提供的这种保证正是让我们无需在普通 JSX 代码中指定键的原因,其中一个 div 可能有多个孩子。

但是我们还没有这种语法。 所以现在这是一个已知的限制。

@JesperTreetop & @zwily@gaearon解释得比我好;)

一旦你知道,这没什么大不了的,但我们都希望 React 蓬勃发展,我只是说......

@gaearon对于<>语法提案是否还有另一个问题,我们可以观看而不是进一步讨论这个问题? 我四处寻找,但我找不到。

@smrq +1 质疑——我关注所有关于那些尴尬限制的事情(一对一的rendrer()结果、键和 JSX 语法或片段),但我知道的唯一票是https://github.com/脸书/jsx/问题/65

我还认为纤维完全可以解决钥匙问题——但看起来这是一个未实现的梦想

钥匙不是“问题”。 :) 它们是任何允许您创建动态列表的视图框架的基本且必要的部分。

有关更详细的说明,请参阅https://facebook.github.io/react/tutorial/tutorial.html#keys

@spicyj实际上这是一个“问题”,因为它会导致开发人员的能源支出增加,并且在没有这种要求的情况下编程视图框架的基本可能性(例如 https://github.com/dfilatov/vidom)

在没有这种要求的情况下对视图框架进行编程是有基本的可能性的(例如 https://github.com/dfilatov/vidom)

vidom 确实在集合中使用键。 没有它,它可能在技术上工作,但它可能会慢得多。 React 在技术上也可以在没有键的情况下工作,但是当你从列表中删除单个项目时,发现一半的组件必须更新是非常出乎意料的。 使用钥匙,只能卸载一项。

@goto-bus-stop vidom 可以使用密钥,但它们不是必需的,没有它们,只有非常大的情况下需要大量更新才会导致真正的性能问题

所以我认为这部分可能是可选的(例如, shouldComponentUpdate )可用于在个别情况下进行性能调整

@veged vidom 在没有钥匙的情况下挣扎的例子

它不知道应该对元素重新排序,因此它丢弃了根组件的每个渲染上的实例。

作为一个非常熟悉虚拟 dom 空间的人 [1]。 我可以说:

  1. 密钥对于类似兄弟姐妹之间可预测的 dom 和状态更新是必需的。
  2. 键通常不是优化,而且 - 事实上 - 通常是相反的。

[1] https://github.com/leeoniya/domvm

但是,这里明确的问题(如@gaearon所述)是数组的完全静态使用以及静态数组和静态 JSX 片段之间的区别

@brigand O 毫无疑问,状态完整组件的重新排序可能会导致一些问题 ;-) 但迫使所有其他情况(在我看来更多)为此而奋斗......看起来有争议

键对 React重要。 即使它们在某些情况下似乎无关紧要(例如,因为下面的组件都是无状态的),也没有什么能阻止您或团队中的其他人在下面几级的某个地方添加有状态的组件(甚至是普通的 DOM 输入)在几个月内。 到那时,您可能会忘记您没有键,因此重新排序可能会导致状态(或输入值)与错误的项目相关联。

这就是为什么我们鼓励您在任何地方为动态列表指定键。 不这样做会导致非常难以追踪的错误,我们认为最好多花 10 秒来指定密钥,而不是花 10 小时来调试为什么在某些极端情况下低于几个级别的组件的状态会变得混乱。

我完全同意当列表是静态的并且从不重新排序时指定键是不方便的。 欢迎您在 JSX repo 上讨论这个问题。 如果您找不到这方面的问题,欢迎您在那里创建一个新问题。

由于该线程有很多订阅者并且该功能已经实现,我想锁定它以避免向很多人发送垃圾邮件通知。 我希望上面的澄清能解决您的问题,但如果没有,欢迎您创建一个新问题,以进一步讨论特定主题。

@smrq在 jsx 存储库中为<>语法创建了一个提案问题: https://github.com/facebook/jsx/issues/84。

我们刚刚发布了对新React.Fragment导出和相关<>语法的支持:
https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

zpao picture zpao  ·  3评论

zpao picture zpao  ·  3评论

varghesep picture varghesep  ·  3评论

jimfb picture jimfb  ·  3评论

trusktr picture trusktr  ·  3评论