出现的问题
下图是循环出的几个事件Dom,场景是可以对事件进行增加和删除,点击增加则会在循环数组中加多一条数据,每条数据带上一个叉叉的符号可以删除某一条对应的循环数据,但是当删除的时候,会发现删除第二个的时候,第三个事件消失了。
在这个问题上我一度以为是index错误的问题,因为前任开发者写的index下标用的是0、2、3的标识来给每个事件,别问我为什么没有1,我也不清楚,就算是四个事件,也会是0、2、3、4,不明白不清楚不知道。四年前的代码,不敢恭维。
插件查看循环的数据
可以看出每个事件的Dom为ActionCom,让我们来看看这两个ActionCom,第一个是idAction0,第二个是idAction3
因为电脑的原因插件有时候一直用不了,当我用插件查看dom的时候,到这里就发现了问题,第二个明明是删除掉的,在插件中确实只存在了0和3这两个dom
利用的是这部分数据来进行循环出dom元素,这里的freData就是下面代码中的actionSelect,可以看到打印出的循环数组和dom是不对应的,那就是一个很明显的问题,数据和视图不一致
js
getActionSelect = () => {
const actionDom = [];
const actionSelect = this.state.actionSelect;
for (let item of actionSelect) {
if (item) {
// let key = new Date().getTime()
actionDom.push(
<ActionCom
ref={this.myRef}
events={this.events}
actionPropOpts={this.userPropOpts}
userGroupOpts={this.userGroupOpts}
changeRelation={this.changeRelation}
actionHandleDelete={this.actionHandleDelete}
actionHandleAdd={this.actionHandleAdd}
idAction={item.id}
len={item.len}
num={item.num}
resetData={this.props.loadData}
actionDateChange={this.actionDateChange}
actionFullChange={this.actionFullChange}
changeFullRelation={this.changeFullRelation}
handleFullDeleteItem={this.handleFullDeleteItem}
actionFreChange={this.actionFreChange}
addSelect={this.addSelect}
/>,
);
}
}
console.log('actionDom', actionDom);
// let actionDom_ = _.cloneDeep(actionDom)
return actionDom;
};
所以就能知道到底是哪里出现了问题,数据和视图的更新没有同步,并且是循环出的数据,很明显算法在循环实现的时候没有准确识别,说到底应该就是没有使用key,reat的diff算法在更新dom的时候没有使用key,会导致不能精确的更新节点的内容
什么是diff算法
React 的 diff 算法大致步骤如下:
- 比较两个顶层元素,如果它们的类型不同,React 会销毁旧的树并构建新的树。
- 如果两个元素类型相同,则会比较它们的属性,以更新 DOM。
- 如果类型相同,但是子元素不同,则 React 会递归比较它们的子元素。
在比较子元素时,React 会使用 keys(如果已提供)来检测哪些子元素保持不变、被移动或被添加或删除。这样可以避免不必要的 DOM 操作。
总的来说,React 的 diff 算法致力于以最高效的方式更新 DOM,使得实际 DOM 操作尽可能地少,从而提高性能和用户体验。
给循环的每个dom加上key
js
getActionSelect = () => {
const actionDom = [];
const actionSelect = this.state.actionSelect;
for (let item of actionSelect) {
if (item) {
// let key = new Date().getTime()
actionDom.push(
<ActionCom
key={item.id}
...
/>,
);
}
}
return actionDom;
};
再用插件来查看,可以看到已经都加上了key的标识
再看看视图对应的实现效果是怎么样的。
可以看到已经能成功的删除第二个节点,因为有了key的对应,diff算法就知道哪个节点应该复用,哪个节点不应该复用