React中重复元素的密钥管理

时间:2019-01-15 09:55:59

标签: reactjs

我需要一个给一些孩子的组件,它将一遍又一遍地重复这些孩子以填满整个屏幕。假设它有一个简单的markdown,如下所示:

<FillScreen items={[
    { id: 'one', content: 'foo' },
    { id: 'two', content: 'bar' }
]} />

在内部,此组件将获取屏幕大小,并最终将这些项目填充到屏幕中。最终的DOM如下所示:

<div class="fill-screen">
   <div>foo</div>
   <div>bar</div>
   <div>foo</div>
   <div>bar</div>
   <div>foo</div>
   <div>bar</div>
   <div>foo</div>
</div>

我遇到的问题是,当此元素计算出需要渲染7个元素时,我不能直接使用items[i].id作为每个元素的键,因为会有重复的键。

我不确定该问题的最佳解决方案是什么,我已经想到了两种我不完全喜欢的解决方案:

解决方案A

在每个键上附加一个循环索引,因此render中的数组包含以下JSX:

   <div key='one-0'>foo</div>
   <div key='two-0'>bar</div>
   <div key='one-1'>foo</div>
   <div key='two-1'>bar</div>
   <div key='one-2'>foo</div>
   <div key='two-2'>bar</div>
   <div key='one-3'>foo</div>

编辑-请注意,这些元素正在生成到数组中,这就是为什么我们需要指定一个key

我看到这种方法有两个问题:

  • 这时,我可能会摆脱原始密钥,而只使用索引,这是绝对不鼓励的。
  • 我的现实生活中的组件,您给该组件提供商品的方式是通过孩子(我在这里过分简化以阐明要点),因此我必须克隆每个元素只是为了更改密钥。

解决方案B

使用片段在循环之间进行分隔

   <Fragment key={0}>
     <div key='one'>foo</div>
     <div key='two'>bar</div>
   </Fragment>
   <Fragment key={1}>
     <div key='one'>foo</div>
     <div key='two'>bar</div>
   </Fragment>
   <Fragment key={2}>
     <div key='one'>foo</div>
     <div key='two'>bar</div>
   </Fragment>
   <Fragment key={3}>
     <div key='one'>foo</div>
   </Fragment>

在这里感觉更干净,但是同时这也不是首先添加片段的原因,所以也许是滥用它们吗?。

是否有更好的解决方案?什么是社区标准?

2 个答案:

答案 0 :(得分:1)

由于您使用的是Fragment,因此我假设您使用的版本> 16。在那种情况下,我认为有另一种(也许更好)的方法可以解决此问题,即返回一个数组数组。像这样:

[
  [
    <div key='one'>foo</div>,
    <div key='two'>bar</div>,
  ],
  [
    <div key='one'>foo</div>,
    <div key='two'>bar</div>,
  ],
  [
    <div key='one'>foo</div>,
  ],
]

实际上,这与您的“解决方案B”“等效”,但是恕我直言,它不太“ hacky”,因为我认为 Fragment并不是要这样使用。让我扩展一下:

在版本16之前,render函数无法返回React Elements数组,这很烦人。在开始可以使用的版本16上,但是Array的元素必须始终具有key,以使React的协调器正常工作,这意味着从render函数返回该值很不好:

[
  <span>foo</span>,
  <span>bar</span>,
] 

这是正确的:

[
  <span key="firstSpan">foo</span>,
  <span key="secondSpan">bar</span>,
] 

但是这样做有点奇怪和烦人。这就是发明Fragment的原因,这样我们就可以返回:

<Fragment>
  <span>foo</span>
  <span>bar</span>
</Fragment>

因此,Fragment的一大优点是我们不必将keys分配给他们的子元素。这就是为什么我发现看到Fragment带有“键控”子项有点不客气的原因。

此外,我想说的是,“选项A”没有任何问题。但是,我认为在大多数情况下,我提出的解决方案将更易于实现。

答案 1 :(得分:0)

用作键的正确方法取决于在父组件(FillScreen)的生命周期中可能发生的变化。

通常不赞成使用索引作为键,因为如果items道具在FillScreen的生命周期内发生更改,那么同一索引现在可以指向另一个项目,但是该组件将不会重新渲染,因为键保持原样。

在这种情况下,您可以简单地使用idindex的组合作为键,但是请记住,如果将项目重新排列在数组中,则用于显示每个项目的组件将被重新安装。 。使用在片段上使用索引键的其他方法也是如此(此处使用片段没有特别的优势)。

类似地,如果在阵列之间插入一个项目,则用于呈现所有后续项目的组件将被丢弃并重新创建。

如果确实要针对上述情况进行优化,则可以通过附加该ID的出现索引来构造键。

<div class="fill-screen">
   <div key="one-0">foo</div>
   <div key="two-0">bar</div>
   <div key="one-1">foo</div>
   <div key="two-1">bar</div>
   <div key="one-2">foo</div>
   <div key="two-2">bar</div>
   <div key="one-3">foo</div>
</div>

反应元素非常轻巧,因此克隆不会产生大量的开销。