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

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

往期好文推荐

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码3 天前
别再前后端各写一套表单校验了
java·后端
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
晨米酱3 天前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
大勇前进3 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu3 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile3 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go