HarmonyOS 鸿蒙Next中组件未同步

HarmonyOS 鸿蒙Next中组件未同步 数据源变化后,List组件内容未同步变化该如何解决

3 回复

开发者你好:

由于未提供具体的复现代码,以下为典型的数据源变化后,List组件内容未同步示例和解决方案,可以尝试按照解决方案解决。如果未能解决你的问题,请提供具体的复现代码。

【背景知识】

  • 渲染控制LazyForEach从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach,框架会根据滚动容器可视区域按需创建组件,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用。
  • 由于对状态管理当前的特性并不了解,许多开发者在使用状态管理进行开发时会遇到UI不刷新、刷新性能差的情况。请参考状态管理合理使用开发指导

【问题定位】 根据代码: OrderModel.ets:

export class OrderListParams {
  communityId: string = ''
  matterName: string = ''
  current: number = 0
  size: number = 15
}

@Observed
class OrderResult {
  records: OrderInfo[] = []
  total: number = -1
  current: number = 0
  pages: number = -1
}

@Observed
export class OrderInfo {
  id: string
  matterFlag: number
  matterFlagName: string
  matterName: string
  matterRemark: string

  constructor() {
    this.id = ''
    this.matterFlag = -1
    this.matterFlagName = ''
    this.matterName = ''
    this.matterRemark = ''
  }
}

LazyDataSource.ets:

class BasicDataSource<T> implements IDataSource {
  private listeners: DataChangeListener[] = [];

  public totalCount(): number {
    return 0;
  }

  public getData(index: number): T | undefined {
    return undefined;
  }

  // ...
}

@Observed
export default class LazyDataSource<T> extends BasicDataSource<T> {
  dataArray: T[] = [];

  // ...
  public pushData(data: T): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }

  public pushArrayData(newData: Array<T>): void {
    this.clear();
    this.dataArray.push(...newData);
    this.notifyDataReload();
  }

  public clear(): void {
    this.dataArray.splice(0, this.dataArray?.length)
    this.notifyDataReload()
  }
}

@Component
struct OrderListItem {
  @ObjectLink data: OrderInfo

  build() {
    Column() {
      Text(this.data.id)
      Text(this.data.matterName)
      Text(this.data.matterFlagName)
      Text(this.data.matterRemark)
    }.onClick(() => {
      this.data.matterFlagName = this.data.matterFlagName + 'z';
    })
  }
}

OrderListItem.ets:

@Component
struct OrderListItem {
  @ObjectLink data: OrderInfo

  build() {
    Column() {
      Text(this.data.id)
      Text(this.data.matterName)
      Text(this.data.matterFlagName)
      Text(this.data.matterRemark)
    }.onClick(() => {
      this.data.matterFlagName = this.data.matterFlagName + 'z';
    })
  }
}
import OrderModel, { OrderInfo, OrderListParams } from './OrderModel';
import { promptAction } from '@kit.ArkUI';
import LazyDataSource from './LazyDataSource';

@Entry
@Component
struct Index {
  @State myDataSource: LazyDataSource<OrderInfo> = new LazyDataSource();

  private getListData(p: OrderListParams) {
    return new Promise<string>((resolve, reject) => {
      setTimeout(() => {
        const gap = ((p.current - 1) * p.size)
        const llt: OrderInfo[] = []
        for (let index = 0 + gap; index < 15 + gap; index++) {
          const tmp = new OrderInfo()
          tmp.id = 'id' + index
          tmp.matterFlag = 1024 + index
          tmp.matterFlagName = 'matterFlagName' + index
          tmp.matterName = 'matterName' + index
          tmp.matterRemark = 'matterRemark' + index
          llt.push(tmp)
        }
        this.myDataSource.pushArrayData(llt)
        resolve('');
      }, 1 * 1000)
    })
  }

  build() {
    Column() {
      Button('刷新').onClick(() => {
        const p = new OrderListParams()
        p.current = 1
        p.size = 15
        this.getListData(p)
      })
      List({ space: 12 }) {
        LazyForEach(this.myDataSource, (item: OrderInfo) => {
          ListItem() {
            OrderListItem({ data: item })
          }
          .width('100%')
          .onClick(() => {
            item.matterRemark = item.matterRemark + 'w'
          })
        }, (item: OrderInfo) => {
          return item.id + item.matterRemark + item.matterFlagName
        })
      }
      .width('100%')
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
  }
}

这段代码中使用了LazyForEach、@Observed以及@ObjectLink方式构建列表内容,并在Index.ets文件中实现了“刷新按钮”的功能。可以看到,该方法调用了LazyDataSource的pushArrayData方法。

在LazyDataSource.ets文件中,pushArrayData方法首先调用clear方法清空dataArray数组,并调用notifyDataReload方法。然后将新数组的数据push到dataArray中,并再次调用notifyDataReload方法。

从背景知识中的渲染控制可以知道,当LazyForEach的数据源发生变化时,LazyForEach会根据每个组件绑定的key进行重建(即Index.ets文件中的第47行)。

【分析结论】 分析Index.ets文件中的getListData方法传入的新数据可知,第47行的key值在调用getListData方法后不会改变,因此调用notifyDataReload方法后,对应的组件只会从缓存中获取,而不是重建,即表现为UI不刷新。

【修改建议】 解题方向有两种:

给数据类型添加一个时间戳或类似的其他变量,并将该变量应用到key值中:

在OrderModel.ets文件中,给OrderInfo对象添加timestamp属性。然后在Index.ets文件的getListData方法中,构造数据时为timestamp赋值。在LazyForEach的keyGenerator中将timestamp关联到组件key值中。这样每次触发“刷新按钮”时,item的key值会更新,从而调用notifyDataReload方法后触发组件重建,使UI刷新。修改后的代码如下:

OrderModel.ets:

@Observed
export class OrderInfo {
  id: string
  // ...
  timestamp: number

  constructor() {
    this.id = ''
    // ...
    this.timestamp = 0
  }
}

Index.ets:

@Entry
@Component
struct Index {
  @State myDataSource: LazyDataSource<OrderInfo> = new LazyDataSource();

  private getListData(p: OrderListParams) {
    setTimeout(() => {
      const gap = ((p.current - 1) * p.size)
      const llt: OrderInfo[] = []
      for (let index = 0 + gap; index < 15 + gap; index++) {
        const tmp = new OrderInfo()
        tmp.id = 'id' + index
        // ...
        tmp.timestamp = new Date().getTime()
        llt.push(tmp)
      }
      this.myDataSource.pushArrayData(llt)
      resolve('')
    }, 1000)
  }

  build() {
    Column() {
      Button('刷新').onClick(() => {
        // ...
      })
      List({ space: 12 }) {
        LazyForEach(this.myDataSource, (item: OrderInfo) => {
          ListItem() {
            OrderListItem({ data: item })
          }
          .width('100%')
          .onClick(() => {
            item.matterRemark = item.matterRemark + 'w'
          })

        }, (item: OrderInfo) => {
          return item.id + item.matterRemark + item.matterFlagName + item.timestamp.toString()
        })
      }
      .width('100%')
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
  }
}
  1. 对LazyDataSource.ets文件中的pushArrayData方法进行修改,不使用notifyDataReload方法进行重载,改用pushData方法触发notifyDataAdd通知列表进行刷新,此方法改动较小。修改后代码如下: LazyDataSource.ets:
class BasicDataSource<T> implements IDataSource {
  // ...
}

@Observed
export default class LazyDataSource<T> extends BasicDataSource<T> {
  // ...
  public pushData(data: T): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }

  public pushArrayData(newData: Array<T>): void {
    this.dataArray.splice(0, this.dataArray?.length);
    let count = newData.length;
    for (let i = 0; i < count; i++) {
      this.pushData(newData[i]);
    }
  }

  public deleteData(index: number): void {
    this.dataArray.splice(index, 1);
    this.notifyDataDelete(index);
  }

  public clear(): void {
    this.dataArray.splice(0, this.dataArray?.length)
    this.notifyDataReload()
  }
}

更多关于HarmonyOS 鸿蒙Next中组件未同步的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,组件未同步通常是由于ArkTS声明式UI框架状态管理机制问题导致。组件依赖的状态变量未正确触发更新时,会出现渲染不同步。可通过以下方式解决:检查@State@Link等装饰器是否正确定义;使用@Watch监听状态变化;确保组件层级结构合理。若使用ForEach渲染动态组件,需确认数据源标识符和键值生成策略正确。

在HarmonyOS Next中,List组件未随数据源变化同步更新的问题,通常涉及数据绑定机制或状态管理。以下是常见解决方案:

  1. 检查数据源状态管理

    • 确保使用@State@Link@Provide等装饰器标记数据源
    • 示例:
      @State items: Array<string> = ['A', 'B', 'C']
      
  2. 使用ArkUI响应式更新

    • 直接修改数组引用触发更新:
      this.items = [...this.items, 'newItem']
      
    • 避免直接修改数组元素(如push),应创建新数组
  3. List组件配置检查

    • 确认List的data参数绑定正确状态变量
    • 实现if/else条件渲染时确保分支覆盖完整
  4. 嵌套数据更新

    • 对于对象数组,使用@Observed@ObjectLink实现深度观察:
      @Observed
      class Item {
        name: string
      }
      
  5. 开发工具调试

    • 通过DevEco Studio的预览器验证数据流
    • 检查Console是否有数据绑定警告

确保数据修改方式符合ArkUI响应式规范,多数同步问题可通过正确使用状态装饰器解决。

回到顶部