关于React中diff算法数据和视图对应问题

出现的问题

下图是循环出的几个事件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 算法大致步骤如下:

  1. 比较两个顶层元素,如果它们的类型不同,React 会销毁旧的树并构建新的树。
  2. 如果两个元素类型相同,则会比较它们的属性,以更新 DOM。
  3. 如果类型相同,但是子元素不同,则 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算法就知道哪个节点应该复用,哪个节点不应该复用

如果觉得有趣或有收获,请关注我的更新,给个喜欢和分享。您的支持是我写作的最大动力!

往期好文推荐

相关推荐
zyplayer-doc5 小时前
VuePress类静态文档站和动态知识库怎么选:两种技术路线的适用场景
javascript·人工智能·后端·安全·智能手机
rannn_1115 小时前
【力扣hot100】链表专题|160、206、234、141、142
java·算法·leetcode·链表·面试·开发
紫禁玄科6 小时前
Shai-Hulud:npm生态的自我复制蠕虫风暴
前端·npm·node.js
东风破_7 小时前
后端API没写好,前端难道干等着吗?
前端
用户938515635077 小时前
从前后端分离到前端接口工程:React + MockJS + Vite 解析
前端·后端·全栈
用户938515635077 小时前
从零在浏览器里跑 DeepSeek-R1:WebGPU + Transformers.js 全链路实战(三)
前端·react.js·typescript
excel7 小时前
当前端行情变差,我们为什么还要坚持?
前端
码事漫谈8 小时前
世界由什么构成——这个问题比你想的更缠人
后端
鸿是江边鸟,曾是心上人8 小时前
快速搭建HTTPS本地开发环境
前端