HarmonyOS 鸿蒙Next中在aboutToAppear里面加一个定时器给复杂数据类型赋值试图不更新
HarmonyOS 鸿蒙Next中在aboutToAppear里面加一个定时器给复杂数据类型赋值试图不更新

在aboutToAppear里面加一个定时器 给复杂数据类型赋值试图不更在 aboutToAppear 生命周期中使用 setTimeout 异步修改数组中对象的属性,但界面没有刷新更新
更多关于HarmonyOS 鸿蒙Next中在aboutToAppear里面加一个定时器给复杂数据类型赋值试图不更新的实战教程也可以访问 https://www.itying.com/category-93-b0.html
【背景知识】
- class-transformer:是一个基于装饰器的轻量级库,主要用于在普通对象与类实例之间进行双向转换、序列化及反序列化。它支持TypeScript和JavaScript,提供零依赖、跨平台特性,并优化了打包体积。
- ObservedV2:是一个能让类中属性变得可观测的装饰器(需要搭配@Trace),可以观测到类中深层属性的变化。
【解决方案】
- 麻烦确认下,您这边获取的数据,是否是从服务器获取的JSON对象,然后强转的IOrderItem类型,如果是的话,需要将JSON对象通过class-transformer进行序列化;
- 您这边使用 onUnread 监听UI不刷新的问题,可以打印下log日志确认是否正常执行,并且打印元数据确认下数据是否更新。
import { plainToClass } from 'class-transformer';
@ObservedV2
class Son {
[@Trace](/user/Trace) age: number = 100;
}
class Father {
son: Son = new Son();
}
@Entry
@Component
struct Index {
father: Father = new Father();
aboutToAppear(): void {
// 获取原始对象(带有 __ob_ 前缀)
let originalObj = this.father;
// 转换为没有 __ob_ 前缀的普通对象
let plainObj = convertKeysToCamelCase(originalObj);
// 使用 class-transformer 转换为实体类型
let b: Father = plainToClass(Father, plainObj);
console.log('转换后的实体类型:', JSON.stringify(b));
}
build() {
Column() {
Text(`${this.father.son.age}`)
.onClick(() => {
this.father.son.age++;
})
}
}
}
export function underscoreToCamelCase(underscoreString: string): string {
// 捕获__ob_替换成 ''
return underscoreString.replace(/(__ob_)/g, (match: string, letter: string): string => {
console.log(letter)
return '';
});
}
export function convertKeysToCamelCase(obj: ESObject): Father {
if (obj && typeof obj === 'object') {
const newObj: ESObject = {};
Object.keys(obj).forEach((key) => {
if (obj.hasOwnProperty(key)) {
const newKey = underscoreToCamelCase(key);
newObj[newKey] = convertKeysToCamelCase(obj[key]);
}
})
return newObj;
} else {
return obj;
}
}
更多关于HarmonyOS 鸿蒙Next中在aboutToAppear里面加一个定时器给复杂数据类型赋值试图不更新的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
【问题背景】:@State装饰的对象属性值更改,UI无法更新
【解决思路】:
你这个属于数组中嵌套的对象属性值发生变化,是观察不到的,也就是UI不会根据值的变化而变化;
你应该要这样子使用
//例如你的serveList长这样,你没贴出来,我这里假设一个,一样的处理的
interface Itest{
num: number
}
//components组件下
[@State](/user/State) serveList:Array<Itest> = [
{
num = 1
},
{
num = 2
},
{
num = 3
}
]
const newVal:Itest = {
num = 33
}
this.serveList[3] = newVal
通过上面的操作已经可以解决您的问题;以下是使用示例以及说明,通过下面的解释应该会对你的学习有所帮助
-
声明Person和Model类。
class Person { public value: string; constructor(value: string) { this.value = value; } } class Model { public value: string; public name: Person; constructor(value: string, person: Person) { this.value = value; this.name = person; } }@State装饰的类型是Model。
// class类型 [@State](/user/State) title: Model = new Model('Hello', new Person('World'));对@State装饰变量的赋值。
// class类型赋值 this.title = new Model('Hi', new Person('ArkUI'));对@State装饰变量的属性赋值。
// class属性的赋值 this.title.value = 'Hi';嵌套属性的赋值观察不到。
// 嵌套的属性赋值观察不到 this.title.name.value = 'ArkUI'; -
当装饰的对象是array时,可以观察到数组本身的赋值和添加、删除、更新数组的变化。例子如下。
声明Model类。
class Model { public value: number; constructor(value: number) { this.value = value; } }@State装饰的对象为Model类型数组时。
// 数组类型 [@State](/user/State) title: Model[] = [new Model(11), new Model(1)];数组自身的赋值可以观察到。
// 数组赋值 this.title = [new Model(2)];数组项的赋值可以观察到。
// 数组项赋值 this.title[0] = new Model(2);删除数组项可以观察到。
// 数组项更改 this.title.pop();新增数组项可以观察到。
// 数组项更改 this.title.push(new Model(12));数组项中属性的赋值观察不到。
// 嵌套的属性赋值观察不到 this.title[0].value = 6; -
当装饰的对象是Date时,可以观察到Date的赋值,以及通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds更新Date的属性,详见装饰Date类型变量。
-
当装饰的变量是Map时,可以观察到Map整体的赋值,以及通过调用Map的接口set, clear, delete更新Map的值。详见装饰Map类型变量。
-
当装饰的变量是Set时,可以观察到Set整体的赋值,以及通过调用Set的接口add, clear, delete更新Set的值。详见装饰Set类型变量。
给一个测试用例看看?我这边是能更新生效的,
interface orderItem{
text: string,
icon: Resource,
num: number,
id: string
}
const newVal: orderItem = {
text: '在线客服',
icon: $r('app.media.service'),
num: 10,
id: '7'
}
//@Component
@State list:Array<orderItem> = []
aboutToAppear(): void {
this.list.push(newVal)
}
Button("测试").onClick(()=>{
const newVal1: orderItem = {
text: '测试测试测试测试',
icon: $r('app.media.service'),
num: 10,
id: '7'
}
this.list[0] = newVal1
})
import { IOrderItem, orderItem } from "../../models/UserItem"
import {
initYsf, onUnread,
} from '@ysf/sdk'
@Entry
@Component
export default struct qiYu {
@State serveList: IOrderItem = [
{
text: '提货卷',
icon: $r('app.media.serve1'),
num: 0,
id: '4'
},
{
text: '虚拟卡密',
icon: $r('app.media.serve2'),
num: 0,
id: '5'
},
{
text: '心愿单',
icon: $r('app.media.serve3'),
num: 0,
id: '6'
},
{
text: '在线客服',
icon: $r('app.media.service'),
num: 0,
id: '7'
}
] //我的服务
aboutToAppear(): void {
this.initQy()
}
async initQy() {
// 初始化,所有 API 必须在初始化之后调用
await initYsf()
// 企业 appKey
const appKey = ''
// 应用 bundleId
const bundleId = ''
// 设置 appKey、bundleId
// 设置用户信息
// 设置未读消息监听
onUnread((res) => {
console.log('【DEMO】当前未读消息总数1111111: ', res.total);
console.log('【DEMO】新增未读消息: ', res.message);
// 在回调中修改状态值
// 创建新数组触发UI更新
this.serveList[3].num = res.total
})
console.log('【DEMO】七鱼初始化完成')
}
build() {
Column() {
Row() {
Text('11').fontColor('#1A1A1A').fontSize(17).fontWeight(800)
}
.height(55)
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.border({ width: { bottom: 2 }, color: '#EDEDED' })
Row() {
ForEach(this.serveList, (item: orderItem, index: number) => {
Column() {
Badge({
// 徽标
count: item.num,
style: {},
position: BadgePosition.RightTop,
}) {
Image(item.icon).width(41).height(42)
}
Text(item.text).fontColor('#5F5F5F').fontSize(13).fontWeight(500)
}.onClick(async () => {
})
}, (item: orderItem) => item.id)
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.margin({ top: 10 })
.padding({ left: 10, right: 10 })
}
.width('100%')
.height(137)
.backgroundColor($r('app.color.white'))
.borderRadius(5)
.padding({ left: 16, right: 13 })
.margin({ top: 10 })
}
}
这个是主要是想实现监听未读消息数据然后通过Badge回显出来
最好贴一下全部的代码就好分析了
好啦你看一下 onUnread是七鱼提供的监听未读消息总数的 然后我在里面改变this.serveList[3].num数据变化了 试图没更新 然后我有试了一下把整个this.serveList[3]改变还行不行,
@State只能监听到数组本身和第一层的变化,你修改了第二层数据肯定不会刷新,可以直接修改this.serveList[3],
那我把setTimeout去除就可以 ,然后我现在试了一下你说的修改整个this.serveList[3] 好像还是不行,
有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html
我看一下怎么改的,
可以绿泡泡吗这个好麻烦,
在HarmonyOS Next中,aboutToAppear生命周期内使用定时器为复杂数据类型赋值后视图不更新,是因为定时器回调函数执行时已脱离ArkUI响应式框架的跟踪范围。需使用@State或@Link装饰器声明复杂数据类型,并在定时器回调中通过this.xxx = newValue方式触发UI同步。若使用对象属性赋值,需结合@Observed和@ObjectLink实现深度观测。
在HarmonyOS Next中,aboutToAppear生命周期内使用setTimeout异步修改复杂数据类型(如数组中的对象属性)时,界面未刷新的问题通常是由于状态管理机制未正确触发导致的。以下是关键原因和解决方案:
-
状态更新机制:HarmonyOS的UI更新依赖于
[@State](/user/State)装饰器的响应式机制。直接修改对象或数组的内部属性(如arr[0].name = 'new value')不会触发重新渲染,因为框架无法检测到这种变更。 -
正确做法:
- 使用不可变数据:重新创建整个数组或对象,而不是直接修改属性。例如:
this.arr = [...this.arr]; // 通过展开运算符创建新数组 - 结合@State装饰器:确保复杂数据类型(如数组或对象)用
[@State](/user/State)装饰,并在修改时赋值新引用。 - 在定时器中强制更新:在
setTimeout回调中调用this.arr = [...this.arr],或使用框架提供的更新方法(如markDirty)。
- 使用不可变数据:重新创建整个数组或对象,而不是直接修改属性。例如:
-
示例代码修正:
[@State](/user/State) arr: Array<{ name: string }> = [{ name: 'initial' }]; aboutToAppear() { setTimeout(() => { // 错误:直接修改属性,界面不更新 // this.arr[0].name = 'updated'; // 正确:创建新数组并赋值 this.arr[0] = { ...this.arr[0], name: 'updated' }; this.arr = [...this.arr]; }, 1000); } -
注意事项:避免在
aboutToAppear中执行耗时操作,必要时使用异步任务但确保状态变更符合响应式规则。
通过确保数据变更生成新引用,可触发组件重新渲染,解决界面不更新问题。


