【问题标题】:Can one add React Keys after instantiation?实例化后可以添加 React Keys 吗?
【发布时间】:2016-08-12 11:08:42
【问题描述】:

我正在制作 React Elements 的集合并展示它们;下面是一个简单的例子,用来描述一个如何修改一个预先存在的实例化元素的问题。

var c = [
  <div>A</div>,
  <div>B</div>,
  // ...
  <div>Z</div>
];

var ListComponents = React.createClass({
    render: function() {
        return <div>{c}</div>;
    }
});

ReactDOM.render(<ListComponents/>, document.getElementById('root'));

虽然上面的代码“有效”,但它会呈现一条我不想忽略的控制台消息:

警告:数组或迭代器中的每个孩子都应该有一个唯一的“key”道具。 检查 `ListComponents` 的渲染方法。 有关详细信息,请参阅 https://fb.me/react-warning-keys。

从表面上看,我可以为c 中的每个元素添加一个唯一的key="…" 字符串并完成它。

但是,这似乎很冗长,特别是因为我有一个索引数组中的数据和一种函数式语言,理论上可以为每个键分配其匹配的索引值,而无需手动输入它作为源文字。

我很想能够做到这一点......

c.forEach( (e,i) => e.key = i );        // ...or call some setter

什么是*正确的* React 方法来做到这一点 - 并且 - 保持代码干净?



附录: ...对于好奇或只想说添加关键字段的人...

我使用的集合实际上是一个元组数组,包含元数据和相应的 React 元素、自定义组件或一些巨大的 JSX 块。上面的示例过于简单化了实际数据的外观及其不规则性。

由于源数据本身很长,经常更新,并且不是由开发人员维护的;它很容易出现遗漏关键字段或重复手动输入的值。因此,希望完全以编程方式完成。我不能指望数据所有者正确地做到这一点。他们无法阅读代码,所以理想情况下,我宁愿不要用大量“编程垃圾”来搞乱数据结构。

对集合进行了几次操作,将某些元素的各种运行放入其他动态创建的包装器中,以便最终的集合在最终显示之前实际上是由一些转换、过滤器和映射生成的。

【问题讨论】:

  • 可能你不能,因为 React 冻结了元素本身及其属性:Object.freeze(element.props); Object.freeze(element);github.com/facebook/react/blob/…
  • 为什么不将数据转换和 JSX 生成步骤分开呢?先准备只包含纯数据的数组c,然后在组件中用c.map((v, i) =&gt; &lt;div key={i}&gt;{v}&lt;/div&gt;)渲染。
  • 这不是“表面上”,而是“遵循 React 的系统进行有效差异化”。 React 不知道你刚刚声明了一个一次性数组,它所看到的只是你传入一个它需要 的元素列表,以便在每次render() 调用时有效地进行差异。它需要这些键来识别发生了哪些元素修改,即使你告诉自己你永远不会改变它们。
  • @zerkms -- 问题在于输入数据的人认为他们正在输入 HTML 并且不懂编程,否则这正是我处理解决方案。唉,真正的数据是异构的,不仅仅是像过于琐碎的例子中所示的 DIV,而是各种元素,包括为它们制作的一些自定义标签。不过,谢谢。
  • 然而,这种方法的问题在于索引不是唯一标识。如果它在您使用 map()sort().map() 时识别出 不同的 元素,那么您的密钥是假密钥,并且您会对 React 有效地进行虚拟 DOM diffing 的能力产生不利影响。如果您对此感到满意,那么就没有问题,但是如果您想做“正确”的事情,则不能使用数组位置来唯一标识项目,因为它们不能。他们以任何方式对条目只字不提,只对条目所在的列表说了些什么。或者只是忽略警告,这也有效。

标签: javascript reactjs functional-programming react-jsx jsx


【解决方案1】:

Wes Bos 致敬,他想出了一个可行的聪明解决方案!

代码是一个简单的单行代码,完全符合我的要求:

c = c.map( (el,key) => React.cloneElement(el, {key} ));

我们正在使用 .cloneElement() 方法构建一个新集合,但我并不知道。事实证明,这正是我所需要的。

.map() 操作中,lambda 函数同时传递了元素和 index。它的返回值是一个克隆的元素,但是设置了 key 属性。

通过巧妙地命名索引元素key,它允许表达式{ "key" : key } 的短符号。该对象增强了克隆对象。

最后,我得到了一个新的相同对象集合,每个对象都有一个键属性设置为索引。

【讨论】:

  • 请注意,就 React 的需求而言,这不是一个好的解决方案:键用于虚拟 DOM 差异,并且应该唯一标识您正在创建的元素,而数组位置则没有。例如,使用正确的键,从 map()sort().map() 将使 React 只需移动一些节点而几乎没有开销,因为它所看到的只是对键的重新排序。使用数组位置键,您已经告诉 React 修改内部属性并完全重新呈现所有条目,因为如果您认为键是“稳定的 id”,那么基于这些键 所有内容都已更改
  • 对于那些跟随的人,我的兴趣是知道如何在事后添加键 - cloneElement 是解决方案。对于我高度专业化的特定高案例,我使用输入数组 ONCE 来生成一组组件 ONCE,并且不以任何方式将其视为容器或动态。按照上面 Mike 的指导,避免使用数组的索引作为键,在正常情况下,这不是正确的做法。问题示例被故意过度琐碎化,并且只关注“修改”预构建元素的概念。这不是真实世界的应用程序代码!
猜你喜欢
  • 2017-12-22
  • 2015-02-28
  • 1970-01-01
  • 1970-01-01
  • 2011-10-22
  • 1970-01-01
  • 2014-07-20
  • 1970-01-01
  • 2018-06-19
相关资源
最近更新 更多