关于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算法就知道哪个节点应该复用,哪个节点不应该复用

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

往期好文推荐

相关推荐
和平宇宙26 分钟前
AI笔记005. hermes-DeepSeek V4 Pro, 128K上下文引发的探索
前端·人工智能·笔记
IT_陈寒40 分钟前
Redis持久化这个坑,我爬了一整天才出来
前端·人工智能·后端
无风听海40 分钟前
多租户系统中的 OIDC:Discovery 端点与联合登录的深度实践
后端·python·flask
naildingding1 小时前
3-ts接口 Interface
前端·typescript
mONESY1 小时前
JavaScript 栈、队列、数组与链表核心知识点总结
javascript·面试
贺国亚1 小时前
电商AI辅助交易场景
面试
小小前端仔LC1 小时前
Node.js + LangChain + React:搭建个人知识库(六)- “吃什么”项目实战:从700+菜谱入库到Taro H5端JSON渲染
前端·后端
chase_my_dream1 小时前
C++ + SLAM 高频面试问题整理
开发语言·c++·面试
晓13131 小时前
【Cocos Creator 3.x】篇——第二章 入门
前端·javascript·游戏引擎
想要成为糕糕手2 小时前
前端必修课:JavaScript 数组与数据结构底层逻辑全解析
javascript·数据结构·面试