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
相关产品推荐
相关产品推荐

