React: 组件的key

参考文档:
React文档:Lists and Keys
React文档:Reconciliation
React 实践心得:key 属性的原理和用法 ——淘宝前端团队

A “key” is a special string attribute you need to include when creating lists of elements.

最早在React文档中看到关于组件的key,当时并没有很在意,直到某天在控制台看到了如下报错信息:

Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `App`. See https://fb.me/react-warning-keys for more information.

那么这个 key 究竟有多重要呢?

React文档中说应当在数组中给组件一个单独的 key ,React 用这个 key 作为组件的身份来识别具体对哪个组件做增删改各种操作。通常使用这个组件的唯一标识来作为key值,例如组件数据的id等:

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

文档中写到,通常来说,不建议使用 index 作为 key 值。
毕竟 index 太易变,如果使用 index 作为 key,某种程度上来说就失去了使用 key 的意义,同时可能还会引发其他的问题。但是,如果你在通过遍历数组生成一组组件时没有为每个组件分配key值,React 会自动使用 index 作为 key 值来分配给每个组件

注意:

  • 关于 key 值的唯一性:

Keys used within arrays should be unique among their siblings. However they don’t need to be globally unique.

也就是说,我们可以在生成两组不同的组件时使用相同的一组 key。

  • key 只提供给 React 使用

Keys serve as a hint to React but they don’t get passed to your components.

const content = posts.map((post) =>
  <Post
    key={post.id}
    id={post.id}
    title={post.title} />
)

虽然 key 在使用时看起来和 props 很像,但你并不能在组件中通过 prop 取到它的值,如上述代码,在 Post 组件中,可以取到 props.id 的值,但 props.key 的值是拿不到的。

看起来没有使用数组 map() 时,你的组件可能也需要 key

认识到 key 的重要性之后,我也曾经天真地以为只需要在 map 中使用它,毕竟React文档好像也只是在强调 map,直到上一个某一天之后不太久的某天……

这次,我遇到了一个略微复杂的需求,大概是要先 map 一个数据数组,加载一个用户列表:

const userList = users.map((user) =>
  <UserItem
    key={user.id}
    id={user.id} />
)

加载用户列表的过程当然每个 item 都需要一个
key 的,我选择用 user.id 作为 UserItem 的 key,然后在与某个 item 发生交互(例如点击)时,把 user.id 传给用户详情组件 <UserInfoCard id={user.id}/>,拿着 user.id 去redux 里查询这个用户信息数据,如果没有就去后端请求这个数据,将后端返回的数据加入redux,再从 redux 中取得数据加载到组件,每次只展示当前点击的 UserItem 对应的 UserInfoCard,但所有的 UserInfo 组件都自动渲染在某一个节点(假设为Container)下。所有 UserInfoCard 一旦 mount,在父节点 Container 未发生变化时不会 unmount,仅会 update 数据和是否可见的属性。

面对这个逻辑,作为 UserInfoCard 的使用者,我没有关心它具体渲染在哪个节点下,自然忽略了它们共有一个父节点,也就是说,虽然没有使用 map 来生成一组 UserInfo 组件,但确确实实每增加一个UserInfoCard,都等同于在它们的父节点Container下为它们增加了一个同为 UserInfoCard 的好姐妹。

这样实际上和 map 生成的一组好兄弟本质上并没有什么区别,没有 key 值的话,React 难以对它们加以区分,另外本地没有的数据还需要从后端获取,假设这样一种情况:

  1. 点击一号用户的 UserItem,需向后端请求一号用户的数据
  2. 在后端的数据返回之前,点击二号用户的 UserItem,二号用户的数据也没有,向后端请求二号用户的数据

从点击一号用户到再点击二号用户的过程,对父节点 Container 来说只是一个
Update 的过程,在点击二号用户之后,收到一号用户的数据之前,对父节点来说,此时它有两个完全一样的React子元素(两个没有数据的 UserInfoCard ),此时收到了一个用户的信息数据,那么把它更新给哪个 UserInfoCard 呢?请求数据这种异步操作无法预料哪一条请求的数据会先到达,这就很容易造成混乱了——可能你点击了二号用户的UserItem,看到的却是一号用户的信息。

为了避免这种混乱,使用 key 属性就非常有必要了。

在使用 UserInfoCard 时将 user.id 作为
key 一并传给 UserInfoCard,这样用户在点击不同的 UserItem 时,key 也发生了变化,结合前面谈到的 key 的作用,不难发现,对 React 来说此时就很容易分辨究竟是哪个 UserInfoCard 需要更新,即使多次点击同一个 UserItem,也不会创建多个相同的 UserInfoCard,而是会更新该 key 对应的 UserInfoCard 实例。

总结:不止在 map 一个数组创建一组组件实例时需要使用 key 为每个数组元素标记,几乎在任何由统一结构或同一组件实例化为一组对象时,都应使用 key 作为它们的身份识别。

参考文档里最后一篇文章提到了在拥有复杂状态(每个状态有具体与之对应的对象)的组件里使用 key 来使组件在适当的时候触发销毁和重建,以此来保持组件内部逻辑的清晰,这也是文档里没有提到的一种对 React key 的应用。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容