Read the original article:Why does @Observed stop working after assigning JSON data from a server
Question
Why does @Observed stop working after assigning server-fetched data to a variable in HarmonyOS?
Short Answer
When JSON data is received from the server, it's plain and lacks class metadata. Directly assigning it to an @Observed-decorated variable breaks reactivity. To fix this, convert the plain object into an actual class instance using class-transformer's plainToClass along with reflect-metadata. Here you can find the related code below;
myList
[{
"id": 100,
"c": 2,
"url": "https://img2.baidu.com/it/u=3271634463,3958449710&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=1083"
}]
view.ets
import myList from './arr.json'
import { Type, plainToClass } from 'class-transformer'
import "reflect-metadata"
let NextID: number = 1;
@Observed
class ClassA {
public id: number;
public c: number;
public url?: string;
constructor(c: number,url?:string) {
this.id = NextID++;
this.c = c;
this.url = url
}
}
@Component
struct ViewA {
@ObjectLink a: ClassA;
label: string = 'ViewA1';
@State obj: ClassA = new ClassA(0)
aboutToAppear(): void {
this.obj = JSON.parse(JSON.stringify(this.a))
}
build() {
Column() {
Button(`ViewA [${this.label}] this.a.c = ${this.a ? this.a.c : "undefined"}`)
.width(320)
.margin(10)
.onClick(() => {
this.a.c += 1;
})
Image(this.a.url? this.a.url: '')
.width(50)
.height(50)
Button(`itemchangeUrl`)
.width(320)
.margin(10)
.onClick(() => {
this.a.url='https://img2.baidu.com/it/u=2352701707,3390340869&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=1083';
this.obj.url ='https://img2.baidu.com/it/u=2352701707,3390340869&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=1083';
console.info('itemchangeurl')
})
Image(this.obj.url? this.a.url: '')
.width(50)
.height(50)
}
}
}
@Entry
@Component
struct ViewB {
@State arrA: ClassA[] = [new ClassA(0,'https://img0.baidu.com/it/u=4133879726,3955771682&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=1083'), new ClassA(0)];
@State url: string = 'https://img0.baidu.com/it/u=4133879726,3955771682&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=1083'
@State picFlag: boolean = true
@State arrFlag: boolean = true
@State list: ClassA[] = []
aboutToAppear(): void {
this.list = this.arrA
}
build() {
Column() {
ForEach(this.list,
(item: ClassA) => {
ViewA({ label: `#${item.id}`, a: item })
},
(item: ClassA): string => item.id.toString() + item.url
)
Button('changeUrl')
.onClick(() => {
if(this.picFlag){
this.list[0].url = 'https://img0.baidu.com/it/u=194882366,1163824946&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667'
this.url = 'https://img0.baidu.com/it/u=194882366,1163824946&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667'
}else {
this.list[0].url = 'https://img2.baidu.com/it/u=2352701707,3390340869&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=1083'
this.url = 'https://img2.baidu.com/it/u=2352701707,3390340869&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=1083'
}
this.picFlag = !this.picFlag
console.info('changeSuccess',this.arrA[0].url)
})
Button('change List')
.onClick(() => {
if(this.arrFlag){
this.list = []
myList.forEach((v) =>{
let tmp = plainToClass(ClassA, v)
this.list.push(tmp)
})
}else {
this.list = this.arrA
}
this.arrFlag = !this.arrFlag
})
}
}
}
Applicable Scenarios
This applies when you're loading lists or objects from a server (e.g., via fetch or from a .json file) and binding them to UI using @Observed, @State, or @ObjectLink. Without proper type restoration, property updates won't trigger UI updates.
Top comments (0)