显然,对于React中的diffing算法来说,***键***是必不可少的,但是我一直在想,为什么React不能根据我们迭代的内容自动生成键呢?
我还假设条目可以共享一些相似性,或者在内容方面可以相同,但是用户打开页面后是否可以生成密钥并以某种方式将其附加到条目上,因此它是稳定的?
或者可能有哪里试图解决问题,如果是这样,我会很感激,如果你分享给我。
更新
谢谢你们的回答,我学到了很多!还有一件事我一直在想:当没有稳定的id时(例如用户添加了一个还没有保存到DB中的条目),我们的开发人员会做什么。在这种情况下,我们只是生成id,并将其附加到对象或数组中的元素,但我们不会即时生成id,因此它会随着时间的推移而保持稳定。
如果React只为渲染过程中涉及的所有数组(换句话说,直接用于渲染函数的数组)生成id,会怎么样?
它只能做一次,在阶段提交阶段,或什么。而且我相信,该id可以是只读的,或什么的,所以用户不能删除该id。
p.s. s当我在写上面的p.s.问题时,我意识到,自动生成数组的id是行不通的,因为我错过了两件事。所有副作用React只能在提交阶段完成,而不能在渲染阶段完成。
主要的问题是当我们在后端使用过滤或排序时,因为我们收到一个新的数组,filtered one,我们需要为这些元素重新生成id,但基本上,这是相同的html元素,我们可以更改内容以匹配过滤顺序,这与 * Slava Knyazev * 提到的相同。
3条答案
按热度按时间2nbm6dog1#
React不能生成键,因为键的全部作用是帮助React在树比较阶段跟踪元素。
例如,假设您有以下代码,其中您天真地使用内容而不是标识符作为键:
上面的代码将按照你的预期运行,但是如果一个人的
name
发生了变化,那么会发生什么呢?为了理解它,让我们看看它生成的树:如果
James
在渲染之间变为Josh
,则新树将如下所示:React将比较两个结果并得出以下结论:
li(James)
将被删除li(Josh)
但是,如果我们将
key
属性设置为p.id
,则旧树和新树将分别如下所示:一个三个三个一个
当React比较两者时,它将识别出
James
已经变成Josh
,并且只需要调整文本。在第一个场景中,
<li>
组件被完全销毁,取而代之的是一个全新的组件。这两个操作都会运行该组件的完整React生命周期。在第二个场景中,该组件保持不变,仅更改其中的文本。虽然在这种人为的场景中,第一种情况下的性能损失很小,但对于复杂的组件,性能损失可能非常大。
juzqafwq2#
我相信,除非您的数据100%肯定地以一种方式排序并且永远不会改变,否则
key={index}
不是一个好的键(我假设您希望自动生成的键是这样的)。在新的beta react文档www.example.com中有更详细的解释https://beta.reactjs.org/learn/rendering-lists#where-to-get-your-key
gpnt7bae3#
我想你的意思是React可能会选择在对象上使用稳定的散列(比如序列化字符串上的sha1)来生成唯一的键。我想这在很多情况下都是可行的,甚至让我停下来思考了一会儿!你的问题真的很好,也很深刻。
然而,它并不是在所有情况下都有效。我认为它只适用于没有方法或任何附加内容的静态对象。适用于JS对象not all properties are enumerable。散列只能发生在属性的可枚举对象上,但dev可能有非可枚举但仍然唯一的方法附加到这些对象上。事实上,即使是可枚举的方法也不能真正可靠地序列化,它们可能是使对象唯一的东西。2更不用说涉及原型继承的可靠散列的复杂性了。
我怀疑这也有一个性能方面。散列是便宜的,但没有那么便宜。大多数情况下可以通过引用对象中的唯一ID来键控,这要便宜得多。当枚举大量对象时,这些事情很重要,所以最好听从开发人员的意见。毕竟,如果你真的想散列它,它只是一个函数调用--当它不工作时,这会给开发人员带来很大的困惑。
这也会限制JSX的表达能力,因为它基本上允许自由格式的JS。您可能必须提供一些低级别的
<React.Map>
组件原语,以便响应提供此默认key
处理,这意味着您在什么可以做和什么不可以做(复杂的函数链)方面有更多的限制。