【译】索引作为键是一种反模式(Index as a key is an anti-pattern)

简介: 【译】索引作为键是一种反模式(Index as a key is an anti-pattern)

写在前头

原文地址

我多次看到开发人员在呈现列表时使用项的索引作为键。

{
    todos.map((todo, index) => (
        <Todo {...todo} key={index} />
    ));
} 
复制代码


它看起来很优雅,而且去掉了警告(这才是“真正的”问题,对吧?)这里的危险是什么?


它可能会破坏你的应用程序和显示错误的数据!

让我解释一下,键是React用来识别DOM元素的唯一工具。如果您将一个项目推入列表或删除中间的内容会发生什么?如果键与之前相同,React假定DOM元素表示与之前相同的组件。但这种情况已不复存在。

微信截图_20221112165314.png


为了演示潜在的危险,我创建了一个简单的示例(带有源代码)

微信截图_20221112165329.png


示例的截图,显示了使用索引作为键的危险。

结果是,当不传递任何东西时,React使用索引作为键,因为它是目前最好的猜测。此外,它还会警告你,这是次优的(它说的话有点令人困惑,是的)。如果你自己提供它(键),React会认为你知道你在做什么,记住这个例子,会导致不可预测的结果。


Better

每一个这样的项目都应该有一个永久的和唯一的属性。理想情况下,应该在创建项时赋值。当然,我说的是一个id。然后我们可以用下面的方法来使用它:

{
    todos.map((todo) => (
        <Todo {...todo} key={todo.id} />
    ));
}
复制代码


注意:首先查看项目的现有属性。有可能他们已经有了可以用作身份证明的东西。

一种方法就是抽象地把编号往上移一步。使用全局索引(global index)可以确保任何两个项目具有不同的id。

let todoCounter = 1;
const createNewTodo = (text) => ({
    completed: false,
    id: todoCounter++,
    text
}
复制代码


Much better

生产解决方案应该使用更健壮的方法来处理项的分布式创建。因此,我推荐nanoid。它快速生成短的非顺序的url友好的唯一id。代码可能如下所示:

import { nanoid } from 'nanoid';
const createNewTodo = (text) => ({
    completed: false,
    id: nanoid(),
    text
}
复制代码


注意:

nanoid文档声明它不应该与React一起使用。他们提到在渲染过程中使用它,这是绝对正确的。在这里,我使用它创建一个稳定的ID。所以不要担心,我们谈论不同的事情,这个建议仍然有效。

TL;DR:为每个项目生成一个唯一的id,并在呈现列表时使用它作为键。


Update: Exception from the rule

很多人问他们是否总是要生成id。其他人提出了一些用例,其中使用索引作为键似乎是合理的。

的确,有时生成新的id是多余的,可以避免。例如,翻译许可条款或贡献者名单。

为了帮助你做出决定,我将这些例子的三个共同点放在一起:

  1. 列表和项是静态的—它们不计算也不更改;
  2. 列表中的项目没有id;
  3. 列表从不重新排序或过滤。

当满足所有这些条件时,您可以安全地将索引用作键。


Update 2: React, Preact, and *react

尽管在这篇文章中我写的是React,但问题并不仅限于它。在类似的库中,比如Preact,危险也存在。然而,效果可能是不同的。

请参阅下面的StackOverflow问题,其中最后一个元素消失了。也请注意Preact的创建者提供的答案中的解释,Jason Miller

Wrong components rendered by Preact


Update 3: nanoid

之前本指南推荐的是shortid.。这个ID生成器被弃用了,所以我用nanoid.代替了它。


Update 4: Clarify nanoid + React usage

正如一些人评论的那样,nanoid不建议直接在React的组件渲染中使用。读完这篇文章,我希望你知道为什么。我添加了一个注释,澄清本文中的建议仍然有效。


参考资料及相关文章


如果你喜欢这篇文章,请不要忘记在下方留言👏。每次鼓掌通知对我来说都是一种激励。 如果你想了解更多,我开设了一个关于JavaScript的YouTube频道,请考虑订阅。从一开始就陪伴我,帮助我变得更好。



目录
相关文章
|
存储 算法 Java
Go 通过 Map/Filter/ForEach 等流式 API 高效处理数据
Go 通过 Map/Filter/ForEach 等流式 API 高效处理数据
|
SQL 关系型数据库 MySQL
MySQL数据库基础练习系列13、用户注册与登录系统
MySQL数据库基础练习系列13、用户注册与登录系统
134 1
|
7天前
|
云安全 人工智能 自然语言处理
|
11天前
|
人工智能 Java API
Java 正式进入 Agentic AI 时代:Spring AI Alibaba 1.1 发布背后的技术演进
Spring AI Alibaba 1.1 正式发布,提供极简方式构建企业级AI智能体。基于ReactAgent核心,支持多智能体协作、上下文工程与生产级管控,助力开发者快速打造可靠、可扩展的智能应用。
991 35
|
5天前
|
机器学习/深度学习 人工智能 自然语言处理
Z-Image:冲击体验上限的下一代图像生成模型
通义实验室推出全新文生图模型Z-Image,以6B参数实现“快、稳、轻、准”突破。Turbo版本仅需8步亚秒级生成,支持16GB显存设备,中英双语理解与文字渲染尤为出色,真实感和美学表现媲美国际顶尖模型,被誉为“最值得关注的开源生图模型之一”。
673 4
|
7天前
|
机器学习/深度学习 人工智能 数据可视化
1秒生图!6B参数如何“以小博大”生成超真实图像?
Z-Image是6B参数开源图像生成模型,仅需16GB显存即可生成媲美百亿级模型的超真实图像,支持中英双语文本渲染与智能编辑,登顶Hugging Face趋势榜,首日下载破50万。
527 25
|
14天前
|
数据采集 人工智能 自然语言处理
Meta SAM3开源:让图像分割,听懂你的话
Meta发布并开源SAM 3,首个支持文本或视觉提示的统一图像视频分割模型,可精准分割“红色条纹伞”等开放词汇概念,覆盖400万独特概念,性能达人类水平75%–80%,推动视觉分割新突破。
859 59
Meta SAM3开源:让图像分割,听懂你的话