首页 > 技术文章 > react的diff算法与antd中switch组件不更新问题

Shyno 2021-07-30 10:35 原文

问题描述:

   现在有个需求,现有一个列表table,里面的数据有启用的也有关闭的,switch组件会根据数据状态展示,同时进行排序,启用数据在前面,未启用的在后面.如图

然后现在需要操作,假如我将第四条数据'的撒管道施工'进行启用,调用启用接口,改变switch组件状态,并重新调用列表查询接口,理想效果,列表数据按启用排序,前三条应该是启用,其他情况是未启用.可实际结果如图

所操作的数据状态确实对了,排序也ok的,但是"第四条数据的switch组件状态没有更新",刷新页面之后,组件更改了.

 

也就是结果是,switch正确切换了,但是第二次回显还是保留了回显前的状态.代码

代码如下:

 {
          title: '是否启用',
          dataIndex: 'whetherUseing',
          key: 'whetherUseing',
          // width: 150,
          render: (text, record, index) => {
            if (text === 1) {
              return (
                <div
                  id={`${record.id}${index}${text}`}
                  // key={`${record.id}${index}${text}`}
                >
                  <Switch
                    defaultChecked={true}
                    disabled={!access_enable}
                    onChange={(value) => startOrEnd(value, record)}
                  />
                </div>
              );
            } else {
              return (
                <p
                  id={`${record.id}${index}${text}`}
                  // key={`${record.id}${index}${text}`}
                >
                  <Switch
                    defaultChecked={false}
                    disabled={!access_enable}
                    onChange={(value) => startOrEnd(value, record)}
                  />
                </p>
              );
            }
          },
        },

 产生现象的原因:

    主要是react的diff算法导致的.diff算法在以下三种情况时会重新构建一个新的DOM树.

   (1)当dom的种类发生变化时,比如div变成了p标签

   (2)当dom的结构发生变化时,比如由原来的父子结构变成了兄弟结构

   (3)当dom的key发生变化时,你可以为dom元素加上key属性,当(1)和(2)不满足时,可以属性发生变化也会导致重新构建dom树

那switch组件这边发生了什么呢?

启用前:switch组件的父元素是一个p标签,打开tab第五栏的开关之后,开关变成了打开状态.接下来就是问题所在,当重新调接口后,该数据确实变成了启用状态,排在了前面(正确),但是,table第四栏的数据是关闭状态,所以逻辑上仍然走else,所以swtich的父元素dom种类没变,dom结构也没变仍是p标签.所以swtich标签以及p标签并不会更新.所以保持了打开状态.其结果就是,明明数据变成了关闭,可switch标签仍是打开状态.简单来说就是.

主体:table的第四栏的swtich(并非是某条数据)    过程:关闭-----(手动打开,此处此时的数据状态仍为关闭,但是swtich的checked属性变成了true)----->>>打开---------(调用接口,数据更新,第四栏的数据更新了,变成了别的数据,而这条数据仍是关闭.switch开关保持原样,checked属性仍为true)------>>打开(更新了,但没完全更新)

 

解决方案:

   给switch标签加上key属性,既然你数据变了,而我也希望你switch也跟着边,那我就把你的key和你的相关数据绑定.根据diff算法特性,只要你key变了,那就会重新渲染

代码:

  {
          title: '是否启用',
          dataIndex: 'whetherUseing',
          key: 'whetherUseing',
          // width: 150,
          render: (text, record, index) => {
            if (text === 1) {
              return (
                <div
                  id={`${record.id}${index}${text}`}
                  key={`${record.id}${index}${text}`}
                >
                  <Switch
                    defaultChecked={true}
                    disabled={!access_enable}
                    onChange={(value) => startOrEnd(value, record)}
                  />
                </div>
              );
            } else {
              return (
                <p
                  id={`${record.id}${index}${text}`}
                  key={`${record.id}${index}${text}`}
                >
                  <Switch
                    defaultChecked={false}
                    disabled={!access_enable}
                    onChange={(value) => startOrEnd(value, record)}
                  />
                </p>
              );
            }
          },
        },

 主体:table的第四栏的swtich(并非是某条数据)    过程:关闭-----(手动打开,此处此时的数据状态仍为关闭,但是swtich的checked属性变成了true)----->>>打开---------(调用接口,数据更新,第四栏的数据更新了,变成了别的数据,而这条数据仍是关闭.在结构和标签类型都不变的情况下,key发生了变化,重新构建渲染)------>>关闭(重新构建一个p标签,并获得 defaultChecked={false} )

推荐阅读