一句话定义
Key 是数组内兄弟元素的稳定身份标识:diff 时 React 用它回答「这个新元素对应旧树的哪一个」,从而正确复用 DOM 与组件状态——而不是按位置盲目配对。
为什么重要
key 写错不会报错、多数时候还「看起来正常」,但会在插入/删除/排序时产生状态错位(输入框内容串行、勾选状态跑到别的行)。这是 React 面试与 code review 的必考题,也是最难靠测试发现的一类 bug。
前置知识
核心概念
- 同层唯一:key 只需在兄弟元素之间唯一,不必全局唯一。
- 稳定:同一元素跨渲染的 key 不能变;绝不用
Math.random()。 - 可预测:来自数据本身的 id(数据库主键、uuid、业务编号)。
- index 作 key:仅在「列表静态、永不重排/插入/删除」时安全。
- key 强制重置:给任意元素加
key可以强制 React 视其为不同元素(连 state 一起重置)——一个正式支持的技巧。
原理 / 机制
为什么 index key 在插入时会错位——逐步追踪:
jsx// 初始:["A", "B"],每个 Row 里有输入框,用户在第一行输入了 "hello"
const before = [
<Row key={0} text="A" />, // 挂着输入框 "hello"
<Row key={1} text="B" />,
];
// 头部插入新项 C:
const after = [
<Row key={0} text="C" />, // React: "key=0 还在,复用!" → 把 props 换成 C
<Row key={1} text="A" />,
<Row key={2} text="B" />, // 全新节点
];
结果:旧 key=0 的 DOM(含你输入的 "hello")被复用且改了文案;key=2 的 B 是新建节点。表面上「数据对了」,实际上输入内容留在了错误的行——因为身份匹配错了。若 key 用 text 或真实 id,React 会正确地「插入新节点、移动旧节点」,状态跟着身份走。
用 key 强制重置状态的合法用法:
jsx// 切换用户时表单彻底重来,而不必手动清空每个字段
<ProfileForm key={userId} userId={userId} />
直观类比
key 是身份证号,位置只是工位号。Diff 时 React 按身份证找人:换工位(移动)人还是那个人(DOM 与状态保留);用 index 作 key 等于「按工位认人」——新同事占了 3 号工位,公司档案就认他是原来 3 号的那位老员工,履历全串了。
实例 / 案例
排序 + 复选框的经典 demo(错误与正确各一遍):
jsxfunction List({ items, reverse }) {
const shown = reverse ? [...items].reverse() : items;
return (
<ul>
{shown.map(item => (
// ❌ key={i}:反转后复选框状态不跟着数据走
// ✅ key={item.id}
<li key={item.id}>
<input type="checkbox" />
{item.title}
</li>
))}
</ul>
);
}
自己复现一次:勾选第二行 → 点反转 → index key 下勾选「留在第二行」,id key 下勾选跟着原来的行走。前者就是 bug。
常见误区
key={Math.random()}/key={Date.now()}:每次渲染 key 全变 → 全列表销毁重建 → 状态丢失 + 性能灾难。- key 传给子组件内部使用:key 不是 prop,子组件里
props.key是 undefined;需要值就另传id={item.id}。 - 以为 key「越全局唯一越好」:同层兄弟唯一即可;全局 uuid 反而暴露数据模型问题。
- 在
.map()外层包裹无 key 的 Fragment:<Fragment>在数组里也需要key(用<React.Fragment key={id}>)。 - 把「控制台有 key 警告」当噪音忽略:警告正是 index-key 陷阱的探测器。
自测题
- 完整描述「头部插入一项、index 作 key」时发生的三步匹配过程,并指出状态错位点。
- 什么时候
key={index}是安全的?说出两个成立的条件。 <ProfileForm key={userId} />为什么能「重置表单」?不用 key 的替代方案是什么(各自的缺点)?- key 需要在整棵应用里唯一吗?「跨列表撞 key」会怎样?
与其他知识点的关系
- 向前:diff 模型(kp-008)的直接应用。
- 向后:与 kp-014 的「状态存哪里」呼应——state 存在「元素实例」上,key 定义实例身份;kp-028 审查清单里必查项;kp-022 里并发渲染对 key 稳定性要求更高。
延伸阅读
- Keeping list items in order with key:<https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key>
- Preserving State(state 与 key 的关系):<https://react.dev/learn/preserving-and-resetting-state>