0%

已完成 0/29 关

教学模式

你可以随时调整。

此设置调整教师的教学方式,无需重新生成课程。

第 10 关 含代码 约 11 分钟

渲染列表:map 转换、filter 过滤与 key 的必要性

渲染列表:map 转换、filter 过滤与 key 的必要性

在网页开发中,最常见的任务之一就是展示一组结构相似的数据:例如好友列表、电商商品、博客文章、聊天评论。

你肯定不希望手动机械地手写 100 遍 <li>...</li> 标签。在 React 中,我们结合 JavaScript 原生数组的 map() 和 filter() 方法,把底层的数据集合直接映射为生动的组件列表。


1. 使用 map() 将数组转换为组件列表

假设我们有一个科学家名单的字符串数组:

const people = [
  '凯瑟琳·约翰逊: 数学家',
  '马里奥·莫利纳: 化学家',
  '穆罕默德·阿卜杜勒·萨拉姆: 物理学家',
  '珀西·朱利安: 化学家',
  '苏布拉马尼扬·钱德拉塞卡: 天体物理学家',
];

我们要把它渲染成一个 HTML 无序列表 <ul>:

export default function List() {
  const listItems = people.map(person =>
    <li>{person}</li>
  );

  return <ul>{listItems}</ul>;
}

JavaScript 基础补课:数组的 map() 方法 array.map(fn) 会遍历数组中的每一项,把每一项传给函数 fn 计算,并将返回值按顺序组装成一个新数组。 在上面的例子中,people 是一个字符串数组,经过 map 转换后,listItems 变成了一个包含 5 个 <li>...</li> JSX 元素的新数组!React 能够直接把 JSX 数组渲染到页面中。


2. 先用 filter() 过滤,再用 map() 渲染

在实际业务中,我们经常只需要展示满足特定条件的一部分项。例如,我们只想单独列出化学家:

const people = [
  { id: 0, name: '凯瑟琳·约翰逊', profession: '数学家' },
  { id: 1, name: '马里奥·莫利纳', profession: '化学家' },
  { id: 2, name: '穆罕默德·阿卜杜勒·萨拉姆', profession: '物理学家' },
  { id: 3, name: '珀西·朱利安', profession: '化学家' },
  { id: 4, name: '苏布拉马尼扬·钱德拉塞卡', profession: '天体物理学家' }
];

export default function ChemistsList() {
  // 1. 过滤出化学家
  const chemists = people.filter(person =>
    person.profession === '化学家'
  );

  // 2. 将过滤后的结果映射为 JSX
  const listItems = chemists.map(person =>
    <li key={person.id}>
      <p>
        <b>{person.name}</b>: {person.profession}
      </p>
    </li>
  );

  return <ul>{listItems}</ul>;
}

3. 为什么必须为列表项提供 key?

如果你运行上面没有加 key 的第一段代码,打开浏览器的控制台,你会看到一个醒目的警告信息:

Warning: Each child in a list should have a unique "key" prop. (警告:列表中的每个子项都应该拥有一个唯一的 “key” 属性。)

什么是 key?

key 是一个特殊的字符串或数字,它就像每个人独一无二的身份证号。

想象一下这个现实生活场景: 一个班级有 10 位同学按学号坐成一排。如果老师突然让第 1 位同学离开教室,剩下的同学向前挪动一个座位:

  • 如果没有名字/身份证号(没有 key):老师只看座位号(索引 0、1、2…),他会以为“原本坐第 1 个座位的同学换了张新脸,坐第 2 个座位的同学换了张新脸…”,导致误判每个人的作业本和书包;
  • 如果每位同学胸前都贴着唯一姓名牌(有 key):老师一眼就能辨认出:“原来只是张三走了,李四、王五都只是换了个座位而已,他们的作业本依然属于他们自己”。

在 React 中也是一样:当列表项发生插入、删除、重排序时,key 告诉 React 哪个节点在历次渲染间是同一个实体。React 借此能够最小化 DOM 操作,并保证子组件内部的状态不会错乱!


4. key 的三大黄金准则

准则一:key 必须在兄弟节点之间保持唯一

不同的兄弟列表项不能拥有相同的 key。但是不同列表(在不同组件或不同父级)之间的 key 可以相同。

准则二:key 必须是稳定不变的

绝对不要在渲染时使用 Math.random() 临时生成 key! 如果每次渲染都生成新的随机数作为 key,React 会认为所有子项全部被销毁重建,导致列表每次打字或更新时都闪烁白屏,并且输入框的光标会不断丢失!

准则三:为什么不要使用数组索引(Index)作为 key?

如果你不写 key,React 默认会使用数组的下标索引(0, 1, 2…)作为 key。 当列表仅仅是静态展示、永远不增删、永远不改变顺序时,使用 index 是勉强可行的。

但如果列表项支持删除、置顶或重新排序,使用 index 会导致灾难性 bug: 假设你在第 0 项输入框里写了“买牛奶”,然后点击删除了第 0 项。 原本的第 1 项(“做作业”)现在的下标变成了 0!React 会误认为“第 0 项还在,只是文本变了”,结果你输入框里的未保存草稿“买牛奶”竟然出现在了“做作业”的格子里!

从哪里获取稳定的 key?

  1. 来自数据库或后端接口的唯一标识符(如 product.id);
  2. 本地新增数据时,使用自增计数器或 crypto.randomUUID() 生成唯一 ID 并固化在数据对象中。

总结

  • 使用 array.map() 把数据对象数组转化为 JSX 元素数组;
  • 使用 array.filter() 过滤出所需子集;
  • 列表项的外层元素必须指定稳定的 key 属性;
  • 好的 key 来源于数据本身的 ID,切忌使用随机数或可变索引 index。
下一站:保持组件纯粹:纯函数原则与副作用隔离 →
© 2026 React 官方教程