文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

React 中的列表渲染要加 key的原因分析

2024-04-02 19:55

关注

在 React 中我们经常需要渲染列表,比如展示好友列表。

常用写法是用 Arrary.prototype.map 方法,将数组形式的数据映射为 JSX.Element 数组,并嵌入到组件要返回的 JSX.Element 中,如下:

function FriendList() {
  const [items, setItems] = useState(['编程网', '小明', '张三']);
  return (
    <ul>
      {items.map((item) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
}

你需要给每个项提供 key 属性作为标识,以区分不同的项。如果你不加 key,React 会警告你:

Warning: Each child in a list should have a unique "key" prop.

为什么需要 key?

在回答这个问题之前,我们先简单了解一下 React 的 DOM Diff 算法原理。

React 会在状态发生变化时,对真实 DOM 树按需批量更新,产生新的 UI。

为此底层做的工作是:将新旧两棵虚拟 DOM 树进行 diff 对比,计算出 patch 补丁,打到真实 DOM 树上。

为了高效,React 的 diff 算法做了限制:

但这样做会有一个问题,如果同级的多节点  只是位置发生了变化 ,但因为相同索引位置对不上,又发现不能复用,就要销毁一棵树并创建一棵新树,实在是太过于低效了。

于是 React 给开发者提供 key 来标记节点,来优化 React diff 算法,告知 React 某个节点其实没有被移除或不能被原地复用,只是换了位置而已,让 React 更新一下位置。

列表渲染不提供 key 会怎样?

不提供 key,React 就无法确定某个节点是否移动了。

React 就只会对比相同位置的两个节点,如果它们类型相同(比如都是 li 元素),就会对比 props 的不同,进行 props 的打补丁。

​因为 列表渲染通常都是相同的类型,所以位置变动时,多半是会触发节点原地复用效果,倒是不用担心树的销毁重建发生。

原地复用在不提供 key 的时候有时候也是能正确渲染的。

除了一种情况,就是 这个节点有自己的内部状态,最经典的莫过于输入框。

function FriendList() {
  const [items, setItems] = useState(['编程网', '小明', '张三']);
  const swap = () => {
    [items[0], items[1]] = [items[1], items[0]];
    setItems([...items]);
  };
  return (
    <div>
      <ul>
        {items.map((item) => (
          <li>{item}<input /></li>
        ))}
      </ul>
      <button onClick={() => { swap(); }}>
        交换
      </button>
    </div>
  );
}

我们给第一和第二个输入框输入内容。

再点击 “交换” 按钮,交换数组第一和第二个元素位置。

然后我们看到 input 前面的文字正确交换了,但是输入框里的内容却没有交换。

​原因是 React 做了原地复用,而 input 没有传 props,不需要打 props 补丁,保持了原样。

这个问题怎么解决?加 key。让 React 知道你的节点需要移动,你得这样写:​

items.map((item) => (
  <li key={item}>{item}<input /></li>
))

不使用 key 的另一个缺点是:因为原地复用会使传入的 props 发生变化,导致不能利用好 React.memo 的组件缓存能力。

列表渲染的 key 用数组索引会怎样?

效果和不使用 key 相同,依旧是新旧节点的相同索引位置对比,但是控制台不会打印警告。

应该用什么值作为 key?

对于节点,你需要用一个唯一的 id 赋值给 key,通常会是数组的 id,比如后端返回的好友列表的好友 id。

const [items, setItems] = useState([
  { id: 5, name: '编程网' },
  { id: 9, name: '小明' },
  { id: 87, name: '张三' },
  { id: 91, name: '编程网' }
]);
const list = items.map((item) => (
  <li key={item.id}>{item.name}</li>
));

如果后端没有返回 id,你可以自己手动用一个 id 生成器补上一个 id,虽然不太优雅就是了。比如:

const items = ['编程网', '张三'];
const genId = (() => {
  let i = 0;
  return () => {
    return i++;
  }
})();
const itemsWithId = items.map(item => ({ id: genId(), val: item }));
// [{id: 0, val: '编程网'}, {id: 1, val: '张三'}]

对了,这个 key 只需要在同一个层级的节点唯一即可,不要求所有层级的 key 都是唯一的。

另外,如果你确保你的列表渲染后直到被销毁,不会有位置上的变化,可以使用数组索引为 key。

结尾

对于列表的渲染,我们有必要提供 key,来对节点进行区分,React 的 DOM Diff 算法会基于 key 进行节点位置的调整,确保一些涉及到内部状态的节点的渲染状态。

通常来说,key 值应该是唯一的,通常来自后端返回的数据。在你确认列表不会发生位置变更时,可以使用数组索引作为 key,以去掉恼人的警告提示。

有一个点需要说明的是,key 并不是列表渲染的专属,普通的节点也可以用 key。

到此这篇关于React 中的列表渲染为什么要加 key的文章就介绍到这了,更多相关React列表渲染 key内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯