key 做了什么事
查阅 React 官方文档,文档上是这样描述 React Key。在 React 中,key 是用于帮助 React 识别列表中每个子元素的特殊属性。当你在渲染列表时,你需要为每个子元素提供一个 key 属性。这个 key 属性帮助React 识别哪些元素发生了变化、添加或移除。React 使用 key 来优化列表的更新性能,减少重新渲染的次数,从而提高应用的性能
从上面一段话中提取关键字,key 帮助 React 识别哪些元素发生了变化、添加或移除,我们重点关注这句话
key 导致了什么问题
假设我们有这样的数据
const msgs = [
{
id: 1,
body: "message1",
},
{
id: 2,
body: "message2",
},
{
id: 2,
body: "message3",
},
];
数组的第二项与第三项的 id 是一样的
现在从数组尾部添加新数据,观察其结果
请看下面例子
export const List = () => {
const [messages, setMessages] = useState(msgs);
const addNewMessage = () => {
setMessages((messages) => [
...messages,
{ id: messages.length, body: `message${messages.length + 1}` },
]);
};
return (
<>
<div>
<button onClick={addNewMessage}>add msg</button>
</div>
<ul>
{messages.map(({ id, body }) => (
<li key={id}>{body}</li>
))}
</ul>
</>
);
};
点击 add msg 按钮,观察界面变化,其结果如下
从数组尾部添加新数据没有任何问题
接着,我们尝试从数组头部添加新数据,会发生什么情况呢?请看下面的测试代码
export const List = () => {
const [messages, setMessages] = useState(msgs);
const insertNewMessage = () => {
setMessages((messages) => [
{ id: messages.length, body: `message${messages.length + 1}` },
...messages,
]);
};
return (
<>
<div>
<button onClick={insertNewMessage}>add msg</button>
</div>
<ul>
{messages.map(({ id, body }) => (
<li key={id}>{body}</li>
))}
</ul>
</>
);
};
原数组第二项的数据每次添加新数据后否在重复添加
为什么会导致问题
在 React 中,如果列表中的 key 值重复,主要是因为 key 是 React 用来确定哪些元素发生变化、哪些需要被重新渲染或删除的标识符。React 在进行虚拟 DOM diff 算法时,依赖 key 来高效地匹配和更新 DOM 元素,而重复的 key 会被 React Fiber 树保留下来,导致数据错乱
如何设置合理的 key
比如我们要遍历 users 数组,代码如下
const List = () => {
return (
<ul>
{users.map((user, index) => (
<li key={index}>{user.name}</li>
))}
</ul>
);
};
如上代码所示,我们不推荐使用 index 来做 key。在动态列表中。虽然数组索引通常是唯一的,但是当列表中的项目被重新排序或者过滤时,索引可能会发生变化,这可能导致React出现错误的更新DOM,甚至导致不必要的重新渲染。
另外,使用索引作为 key 还会导致性能问题。当你添加或者删除一个项目时,React 需要重新计算整个列表中每个项目的 key,这可能会导致性能下降
最好的做法是使用每个项目自身的唯一标识作为 key,比如 users 的 user id。这样可以确保 key 在列表中的唯一性,并且在列表项目重新排序或者过滤时不会出现问题
如何需要遍历的数组中没有唯一标识,那么我们根据具体情况创建一个唯一标识