HarmonyOS鸿蒙Next中@State装饰器,置空数据后,打印显示为空,但是界面还是留有数据

HarmonyOS鸿蒙Next中@State装饰器,置空数据后,打印显示为空,但是界面还是留有数据 页面上有个清除按钮,全选数据后,点击清除。判断是否全选,如果全选,则置空数据并且将全选框置为非勾选状态。现在的问题是,清除后,日志显示数据为空,但是界面留有数据。关键点:如果删除this.isSelectedAll = false这行代码,则页面是正常的。这是什么原因?

api版本12,真机和模拟器都能复现。

import { promptAction } from '@kit.ArkUI'

interface ICodeAndName {
  code: string
  name: string
  isSelected: boolean
}

@Entry
@Component
struct Index {
  [@State](/user/State) listDataArr: ICodeAndName[] = []
  [@State](/user/State) isSelectedAll: boolean = false


  async clear() {
    const index = this.listDataArr.findIndex(item => item.isSelected === true)

    if (index === -1) {
      promptAction.showToast({ message: '请勾选要清除的数据后再重试!' })
      return
    }
    const i = await promptAction.showDialog({
      title: 'showDialog Title Info',
      message: 'Message Info',
      buttons: [
        {
          text: '取消',
          color: '#000000'
        },
        {
          text: '确定',
          color: '#000000'
        }
      ]
    })
    if (i.index === 1) {
      if (this.isSelectedAll) {
        this.listDataArr = []
        this.isSelectedAll = false
        console.log(JSON.stringify(this.listDataArr))
      }
    }

  }


  aboutToAppear(): void {
    this.listDataArr = [{
      code: '1',
      name: '1',
      isSelected: false
    }]
  }


  build() {
    Column() {

      Column() {

        Text() {
          Span('已经绑定')
          Span(`${this.listDataArr.length}`)
            .fontColor(Color.Red)
          Span('条: ')
        }
        .width('100%')
        .margin({ top: 15 })
        .padding({ left: 50 })
        .fontSize(20)
        .fontWeight(600)
      }
      .width('100%')
      .padding({ top: 15, bottom: 10 })

      Column() {
        Row() {
          Column() {
            CheckboxGroup({ group: 'checkboxGroup' })
              .checkboxShape(CheckBoxShape.ROUNDED_SQUARE)
              .height(16)
              .aspectRatio(1)
              .selectAll($$this.isSelectedAll)
          }
          .width(40)

          Text('编码')
            .layoutWeight(1)
            .fontSize(24)
            .textAlign(TextAlign.Center)
            .border({ width: { left: 1, right: 1 } })
            .padding({ top: 5, bottom: 5 })
          Text('名称')
            .layoutWeight(1)
            .fontSize(24)
            .textAlign(TextAlign.Center)
            .padding({ top: 5, bottom: 5 })
        }
        .width('100%')
        .border({ width: { bottom: 1 } })
        .backgroundColor('#E4EAFF')

        List() {
          ForEach(this.listDataArr, (item: ICodeAndName, index: number) => {
            ListItem() {
              Row() {
                Row() {
                  Checkbox({ group: 'checkboxGroup' })
                    .shape(CheckBoxShape.ROUNDED_SQUARE)
                    .height(16)
                    .aspectRatio(1)
                    .select(item.isSelected)
                    .onChange(() => {
                      this.listDataArr[index] = {
                        code: item.code,
                        name: item.name,
                        isSelected: !item.isSelected
                      } as ICodeAndName
                    })
                }
                .justifyContent(FlexAlign.Center)
                .width(40)

                Row() {
                  Text(item.code)
                    .layoutWeight(1)
                    .fontSize(19)
                    .textAlign(TextAlign.Center)
                    .border({ width: { left: 1, right: 1 } })
                    .padding({
                      top: 6,
                      bottom: 6,
                      left: 4,
                      right: 4
                    })
                  Text(item.name)
                    .layoutWeight(1)
                    .fontSize(19)
                    .textAlign(TextAlign.Center)
                    .padding({
                      top: 6,
                      bottom: 6,
                      left: 4,
                      right: 4
                    })
                }
                .layoutWeight(1)
              }
              .width('100%')
              .border({ width: { bottom: 1 } })
            }
          })
        }
        .width('100%')
        .layoutWeight(1)

      }
      .width('95%')
      .layoutWeight(1)
      .border({ width: 1 })
      .margin({ top: 10, bottom: 10 })

      Row() {
        Text('清除')
          .buttonExtend()
          .onClick(() => {
            this.clear()
          })
        Text('提交')
          .buttonExtend()
          .onClick(() => {
          })
      }
      .width('100%')
      .padding({ left: 40, right: 40 })
      .justifyContent(FlexAlign.SpaceBetween)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F9')
    .padding({ bottom: 20 })
  }
}

@Extend(Text)
function buttonExtend() {
  .width(100)
  .height(35)
  .textAlign(TextAlign.Center)
  .backgroundColor('#126DDE')
  .fontSize(20)
  .fontColor('#fff')
  .borderRadius(5)
}

更多关于HarmonyOS鸿蒙Next中@State装饰器,置空数据后,打印显示为空,但是界面还是留有数据的实战教程也可以访问 https://www.itying.com/category-93-b0.html

15 回复

代码修改完毕,请查收!

import { promptAction } from '@kit.ArkUI'

interface ICodeAndName {
  code: string
  name: string
  isSelected: boolean
}

@Entry
@Component
struct Index {
  @State listDataArr: ICodeAndName[] = []
  @State isSelectedAll: boolean = false


  async clear() {
    const index = this.listDataArr.findIndex(item => item.isSelected === true)

    if (index === -1) {
      promptAction.showToast({ message: '请勾选要清除的数据后再重试!' })
      return
    }
    const i = await promptAction.showDialog({
      title: 'showDialog Title Info',
      message: 'Message Info',
      buttons: [
        {
          text: '取消',
          color: '#000000'
        },
        {
          text: '确定',
          color: '#000000'
        }
      ]
    })
    if (i.index === 1) {
      if (this.isSelectedAll) {
        this.listDataArr.length = 0
        this.isSelectedAll = false
        console.log(JSON.stringify(this.listDataArr))
      }
    }
  }


  aboutToAppear(): void {
    this.listDataArr = [{
      code: '1',
      name: '1',
      isSelected: false
    }]
  }


  build() {
    Column() {

      Column() {

        Text() {
          Span('已经绑定')
          Span(`${this.listDataArr.length}`)
            .fontColor(Color.Red)
          Span('条: ')
        }
        .width('100%')
        .margin({ top: 15 })
        .padding({ left: 50 })
        .fontSize(20)
        .fontWeight(600)
      }
      .width('100%')
      .padding({ top: 15, bottom: 10 })

      Column() {
        Row() {
          Column() {
            CheckboxGroup({ group: 'checkboxGroup' })
              .checkboxShape(CheckBoxShape.ROUNDED_SQUARE)
              .height(16)
              .aspectRatio(1)
              .selectAll($$this.isSelectedAll)
          }
          .width(40)

          Text('编码')
            .layoutWeight(1)
            .fontSize(24)
            .textAlign(TextAlign.Center)
            .border({ width: { left: 1, right: 1 } })
            .padding({ top: 5, bottom: 5 })
          Text('名称')
            .layoutWeight(1)
            .fontSize(24)
            .textAlign(TextAlign.Center)
            .padding({ top: 5, bottom: 5 })
        }
        .width('100%')
        .border({ width: { bottom: 1 } })
        .backgroundColor('#E4EAFF')

        List() {
          ForEach(this.listDataArr, (item: ICodeAndName, index: number) => {
            ListItem() {
              Row() {
                Row() {
                  Checkbox({ group: 'checkboxGroup' })
                    .shape(CheckBoxShape.ROUNDED_SQUARE)
                    .height(16)
                    .aspectRatio(1)
                    .select(item.isSelected)
                    .onChange(() => {
                      if (this.listDataArr[index]) {
                        this.listDataArr[index].isSelected = !item.isSelected
                      }
                    })
                }
                .justifyContent(FlexAlign.Center)
                .width(40)

                Row() {
                  Text(item.code)
                    .layoutWeight(1)
                    .fontSize(19)
                    .textAlign(TextAlign.Center)
                    .border({ width: { left: 1, right: 1 } })
                    .padding({
                      top: 6,
                      bottom: 6,
                      left: 4,
                      right: 4
                    })
                  Text(item.name)
                    .layoutWeight(1)
                    .fontSize(19)
                    .textAlign(TextAlign.Center)
                    .padding({
                      top: 6,
                      bottom: 6,
                      left: 4,
                      right: 4
                    })
                }
                .layoutWeight(1)
              }
              .width('100%')
              .border({ width: { bottom: 1 } })
            }
          }, (item: ICodeAndName) => item.code)
        }
        .width('100%')
        .layoutWeight(1)

      }
      .width('95%')
      .layoutWeight(1)
      .border({ width: 1 })
      .margin({ top: 10, bottom: 10 })

      Row() {
        Text('清除')
          .buttonExtend()
          .onClick(() => {
            this.clear()
          })
        Text('提交')
          .buttonExtend()
          .onClick(() => {
          })
      }
      .width('100%')
      .padding({ left: 40, right: 40 })
      .justifyContent(FlexAlign.SpaceBetween)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F9')
    .padding({ bottom: 20 })
  }
}

@Extend(Text)
function buttonExtend() {
  .width(100)
  .height(35)
  .textAlign(TextAlign.Center)
  .backgroundColor('#126DDE')
  .fontSize(20)
  .fontColor('#fff')
  .borderRadius(5)
}

更多关于HarmonyOS鸿蒙Next中@State装饰器,置空数据后,打印显示为空,但是界面还是留有数据的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


主要是修改了这一行代码: this.listDataArr[index].isSelected = !item.isSelected

不过这样onChange的时候,还是会报错,毕竟你已经把所有数据都删除了,是找不到this.listDataArr[index]数据项的,需要增加一点安全校验哦!,

if (this.listDataArr[index]) {
    this.listDataArr[index].isSelected = !item.isSelected
}

还有一个办法

CheckboxGroup({ group: 'checkboxGroup' })
  .checkboxShape(CheckBoxShape.ROUNDED_SQUARE)
  .height(16)
  .aspectRatio(1)
  // .selectAll(this.isSelectedAll)
  .onClick(()=>{
    this.isSelectedAll = !this.isSelectedAll
  })

这个不行,我刚试了。我在多选框加了条件判断,然后可以了,但是我不理解,

啊,我这里可以啊……,

我重新复制代码在模拟器跑了下,也可以了。。。。,

if (i.index === 1) {
  if (this.isSelectedAll) {
    this.isSelectedAll = false;
    setTimeout(()=>{
      this.listDataArr = [];
      console.log(JSON.stringify(this.listDataArr))
    },10)
  }
}

这么写就可以了

没太懂为啥要加个延时器来造异步,

emmmm因为你这里是多选框……反正你就这么写就可以了,

可能组件缓存导致残留**,**List/ForEach 组件可能因复用机制保留旧节点。需通过 if 条件渲染 或 visibility 属性 显式控制显隐:

// 方案1:if 条件控制(完全移除节点)
if (this.dataList.length > 0) {
  List() {
    ForEach(this.dataList, ...)
  }
} else {
  Text('空状态提示')
}

// 方案2:visibility 控制(保留布局占位)
List()
  .visibility(this.dataList.length > 0 ? Visibility.Visible : Visibility.None)

没作用,

@State装饰器在鸿蒙Next中置空数据后界面仍显示旧数据,是因为UI更新机制基于状态变更检测。当数据被置空时,如果UI组件未触发重新渲染,可能仍保留之前的状态缓存。需要确保数据变更后触发UI刷新,例如通过修改状态变量或调用相关更新方法。

这个问题是由于在同一个UI更新周期内,连续修改两个@State变量时,ArkUI框架的更新机制导致的。

根本原因分析:

  1. @State变量的更新机制:当@State变量被修改时,ArkUI会触发UI重新渲染。但多个@State变量的修改如果在同一个微任务周期内,框架可能会进行批量更新优化。

  2. 代码执行顺序问题

    this.listDataArr = []  // ① 清空数组
    this.isSelectedAll = false  // ② 修改全选状态
    

    当执行这两行代码时,ArkUI检测到listDataArr的变化,会触发ForEach重新渲染。但由于isSelectedAll也在同一周期被修改,框架可能将这两个更新合并处理。

  3. CheckboxGroup的selectAll绑定CheckboxGroupselectAll($$this.isSelectedAll)绑定了isSelectedAll状态。当isSelectedAll被设置为false时,CheckboxGroup会尝试取消所有复选框的选中状态。

  4. 竞态条件:在ForEach基于空数组重新渲染的同时,CheckboxGroup正在尝试取消选中已经不存在的复选框项,这可能导致UI状态不一致。

解决方案:

将两个状态更新分开到不同的执行周期:

if (this.isSelectedAll) {
  this.listDataArr = []
  console.log(JSON.stringify(this.listDataArr))
  
  // 使用setTimeout或Promise将isSelectedAll的更新放到下一个微任务
  setTimeout(() => {
    this.isSelectedAll = false
  }, 0)
  
  // 或者使用Promise
  // Promise.resolve().then(() => {
  //   this.isSelectedAll = false
  // })
}

替代方案:

如果希望保持同步更新,可以调整代码逻辑顺序:

if (this.isSelectedAll) {
  this.isSelectedAll = false  // 先取消全选状态
  this.listDataArr = []       // 再清空数组
  console.log(JSON.stringify(this.listDataArr))
}

这样确保在清空数组前,CheckboxGroup已经处理完取消全选的逻辑,避免状态冲突。

回到顶部