React 列表渲染与 key
解释列表协调中 key 的身份作用,说明 index key 的风险与稳定 id 的工程实践。
#type / concept
#status / growing
#tech / dev / frame
#resource / react
[!info] 关联笔记
- 所属:React 基础 MOC
- 路线:React 学习路线
- 相关:渲染与重渲染 · 列表性能
React 列表渲染与 key
这个概念为什么出现
库存表格增删排序后,输入框“串行”、动画错位、组件状态贴错 SKU——根因常常是 key 没有代表稳定身份。key 不是“去掉 warning 的装饰”,而是协调器识别子节点的身份证。
[!abstract] 一句话理解
key告诉 React:这一项是“同一个逻辑实体”还是“新实体”;身份错了,状态就会跟错人。
最小可运行示例
场景:可编辑的低库存列表
import { useState } from 'react'
type Row = { id: string; name: string }
export function LowStockEditor() {
const [rows, setRows] = useState<Row[]>([
{ id: 'A-01', name: '缠绕膜' },
{ id: 'A-09', name: '防潮纸板' },
])
function reverse() {
setRows((prev) => [...prev].reverse())
}
return (
<div>
<button type="button" onClick={reverse}>反转排序</button>
<ul>
{rows.map((row, index) => (
// 教学点:用稳定 id;若用 index,反转后输入状态会串到另一行
<li key={row.id}>
<label>
{row.id}
<input defaultValue={row.name} />
</label>
{/* 反例对比:key={index} */}
</li>
))}
</ul>
</div>
)
}
操作:在第一行输入备注 → 点反转 → 观察输入是否还贴在正确 SKU 上。
结合场景关注点
- 本地 state/非受控输入最容易暴露错误 key。
- 排序/过滤/插入中间元素时 index key 最危险。
- key 应在兄弟列表内唯一,不是全局 UUID 仪式。
核心模型
协调时 React 比较同层子节点:
- 相同 type + 相同 key → 视为可复用更新
- key 变了 → 更可能拆旧挂新,state 重置
边界
- 静态永不重排的列表用 index 风险较低,但习惯上仍优先业务 id
- key 不能神奇加速;错误 key 只会制造 bug
- 不要用随机数每次渲染新 key
常见误区
[!warning] 常见误区:key={Math.random()} 每次都是新身份,组件不断卸载挂载,性能与状态双崩。
工程实践
- 后端实体用主键;前端临时项用客户端稳定 id
- 虚拟列表场景 key 仍要稳定
- 动画库更依赖正确身份
本节总结
列表的正确性先于性能:先让 key 代表身份,再谈 memo 与虚拟化。
自测题
- 为什么过滤列表时 index key 会出问题?
- key 写在
<li>还是子组件根上?
参考答案
- 过滤改变索引与实体对应关系,状态易贴错。
- 写在数组返回的最外层元素上(map 直接返回的根)。