问题描述
项目中有一个列表渲染,点击搜索按钮后,列表会根据输入的内容进行过滤。但是我用了名字字段作为key,这个名字是可以重复的,点击搜素后,数据并不是想要的结果,多了一些接口外的数据。例如下方代码: 页面默认显示数据1,数据1-重复,执行点击事件后,理论上页面显示一个数据2,但是实际显示的是数据1、数据2
jsx
import { useState } from "react";
import styles from "./app.module.css";
function App() {
const [arr, setArr] = useState([
{
label: "数据1",
value: 1,
},
{
label: "数据1-重复",
value: 1,
},
]);
return (
<div className={styles.card}>
<button
onClick={() =>
setArr([
{
label: "数据2",
value: 2,
},
])
}
>
改变
</button>
<div>
{arr.map((item) => (
<div key={item.value} className={styles.card_item}>
{item.label}
</div>
))}
</div>
</div>
);
}
export default App;
问题原因
这个问题主要是因为react diff算法引起的,具体是多节点比较时,发生了问题。react多节点比较主要是reconcileChildrenArray这个函数,会进行2轮遍历,第一轮遍历newChildren,判断对应新旧的key是否一致,下面代码中新的newChildren的一个key是2,old是1,不能复用newFiber为null,就会结束第一轮遍历。
接下来会判断新老节点是否遍历完成,例子中新老都没有遍历完成。然后会把剩余旧节点,以key||index作为Map的key,对应fiber为value。注意这一个操作会把数据1-重复替换数据1。 第二次会遍历剩余新节点,发现在map中没有可以复用的,结束第二轮遍历,然后遍历Map删除旧的剩余节点,比如数据1-重复。
所以最后剩下了数据1,本应该删除的,但是遗留下来了。 vue是没有这个问题的,react为什么不解决这个问题呢?