You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件key属性缺失引发的非性能类Bug及示例问询

React组件key属性的常见Bug及代码示例

我是React新手,正在学习组件的key属性。根据React官方文档(重点标注部分):

你可能会想用数组中项的索引作为它的key。实际上,如果你不指定key,React就会这么做。但当你插入、删除项或数组重新排序时,渲染项的顺序会随时间改变。用索引作为key通常会导致微妙且令人困惑的Bug。

但除了性能问题外,还会出现哪些类型的Bug?我希望看到因缺失key属性而存在Bug的具体代码示例,而非“会有Bug”这类抽象文字说明。例如,下面这段代码即使不使用key也能正常运行:

import { useState } from "react";

export default function MyApp() {
    const [l, setL] = useState([
        {
            id: 0,
            name: "Apple",
        },
        {
            id: 1,
            name: "Orange",
        },
        {
            id: 2,
            name: "Grapes",
        },
    ]);

    return (
        <>
            <ul>
                {
                    l.map(e => {
                        return (
                            <div>
                                <li>{e.name}</li>
                                <button onClick={() => {
                                    const index = l.indexOf(e);
                                    let ll = [...l];
                                    ll.splice(index, 1);
                                    setL(ll);
                                }}>x</button>
                            </div>
                        );
                    })
                }
            </ul>
        </>
    );
}

常见Bug类型及代码示例

1. 表单输入值错位Bug

当列表项包含表单控件时,使用索引作为key(或不指定key)会导致输入内容与列表项不匹配。

import { useState } from "react";

export default function BuggyInputList() {
  const [items, setItems] = useState([
    { id: 1, text: "第一项" },
    { id: 2, text: "第二项" },
    { id: 3, text: "第三项" }
  ]);

  const addItem = () => {
    const newItem = { id: Date.now(), text: `新项 ${items.length + 1}` };
    setItems([newItem, ...items]);
  };

  const deleteItem = (id) => {
    setItems(items.filter(item => item.id !== id));
  };

  return (
    <div>
      <button onClick={addItem}>添加新项</button>
      <ul>
        {items.map((item, index) => (
          <li key={index}>
            <input type="text" placeholder="输入内容" />
            <span>{item.text}</span>
            <button onClick={() => deleteItem(item.id)}>删除</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

Bug复现步骤:

  • 在第二项的输入框中输入“测试文本”
  • 点击第二项的删除按钮
  • 此时原第三项变为第二项,但输入框里仍保留“测试文本”,和显示的“第三项”文本完全不匹配。

原因:React用索引识别组件,删除第二项后,原第三项的索引从2变为1,React会直接复用原来索引1的组件(即之前的第二项),输入框的状态不会重置,导致内容错位。

2. 组件内部状态与数据不匹配Bug

当列表项包含独立内部状态时,索引key会导致状态和对应的数据绑定错误。

import { useState } from "react";

function ListItem({ item }) {
  const [isChecked, setIsChecked] = useState(false);

  return (
    <li>
      <input
        type="checkbox"
        checked={isChecked}
        onChange={() => setIsChecked(!isChecked)}
      />
      <span>{item.text}</span>
    </li>
  );
}

export default function BuggyStateList() {
  const [items, setItems] = useState([
    { id: 1, text: "任务1" },
    { id: 2, text: "任务2" },
    { id: 3, text: "任务3" }
  ]);

  const reverseList = () => {
    setItems([...items].reverse());
  };

  return (
    <div>
      <button onClick={reverseList}>反转列表</button>
      <ul>
        {items.map((item, index) => (
          <ListItem key={index} item={item} />
        ))}
      </ul>
    </div>
  );
}

Bug复现步骤:

  • 勾选“任务2”的复选框
  • 点击“反转列表”按钮
  • 列表变为“任务3、任务2、任务1”,但勾选状态却出现在“任务2”的位置(原任务3的索引位置),状态和任务完全不对应。

原因:列表反转后,每个项的索引发生变化,React根据索引复用组件,原索引1的组件(对应任务2)现在绑定任务3,内部的勾选状态也被带过去,造成错位。


正确做法

使用数据中每个项的唯一标识作为key,比如示例中的item.id,这样React能准确识别每个组件,避免上述所有Bug。

内容的提问来源于stack exchange,提问作者ynn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 12:42:51