HarmonyOS NEXT应用开发之@State装饰器:组件内状态,2024年最新美团HarmonyOS鸿蒙研发岗二面

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
img

正文

使用场景

装饰简单类型的变量

以下示例为@State装饰的简单类型,count被@State装饰成为状态变量,count的改变引起Button组件的刷新:

  • 当状态变量count改变时,查询到只有Button组件关联了它;
  • 执行Button组件的更新方法,实现按需刷新。

@Entry
@Component
struct MyComponent {
@State count: number = 0;

build() {
Button(click times: ${this.count})
.onClick(() => {
this.count += 1;
})
}
}

装饰class对象类型的变量
  • 自定义组件MyComponent定义了被@State装饰的状态变量count和title,其中title的类型为自定义类Model。如果count或title的值发生变化,则查询MyComponent中使用该状态变量的UI组件,并进行重新渲染。
  • EntryComponent中有多个MyComponent组件实例,第一个MyComponent内部状态的更改不会影响第二个MyComponent。

class Model {
public value: string;

constructor(value: string) {
this.value = value;
}
}

@Entry
@Component
struct EntryComponent {
build() {
Column() {
// 此处指定的参数都将在初始渲染时覆盖本地定义的默认值,并不是所有的参数都需要从父组件初始化
MyComponent({ count: 1, increaseBy: 2 })
.width(300)
MyComponent({ title: new Model(‘Hello World 2’), count: 7 })
}
}
}

@Component
struct MyComponent {
@State title: Model = new Model(‘Hello World’);
@State count: number = 0;
private increaseBy: number = 1;

build() {
Column() {
Text(${this.title.value})
.margin(10)
Button(Click to change title)
.onClick(() => {
// @State变量的更新将触发上面的Text组件内容更新
this.title.value = this.title.value === ‘Hello ArkUI’ ? ‘Hello World’ : ‘Hello ArkUI’;
})
.width(300)
.margin(10)

Button(Click to increase count = ${this.count})
.onClick(() => {
// @State变量的更新将触发该Button组件的内容更新
this.count += this.increaseBy;
})
.width(300)
.margin(10)
}
}
}

从该示例中,我们可以了解到@State变量首次渲染的初始化流程:

  1. 使用默认的本地初始化:

@State title: Model = new Model(‘Hello World’);
@State count: number = 0;

  1. 对于@State来说,命名参数机制传递的值并不是必选的,如果没有命名参数传值,则使用本地初始化的默认值:

class C1 {
public count:number;
public increaseBy:number;
constructor(count: number, increaseBy:number) {
this.count = count;
this.increaseBy = increaseBy;
}
}
let obj = new C1(1, 2)
MyComponent(obj)

装饰Map类型变量

说明:
从API version 11开始,@State支持Map类型。

在下面的示例中,message类型为Map<number, string>,点击Button改变message的值,视图会随之刷新。

@Entry
@Component
struct MapSample {
@State message: Map<number, string> = new Map([[0, “a”], [1, “b”], [3, “c”]])

build() {
Row() {
Column() {
ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
Text(${item[0]}).fontSize(30)
Text(${item[1]}).fontSize(30)
Divider()
})
Button(‘init map’).onClick(() => {
this.message = new Map([[0, “a”], [1, “b”], [3, “c”]])
})
Button(‘set new one’).onClick(() => {
this.message.set(4, “d”)
})
Button(‘clear’).onClick(() => {
this.message.clear()
})
Button(‘replace the first one’).onClick(() => {
this.message.set(0, “aa”)
})
Button(‘delete the first one’).onClick(() => {
this.message.delete(0)
})
}
.width(‘100%’)
}
.height(‘100%’)
}
}

装饰Set类型变量

说明:
从API version 11开始,@State支持Set类型。

在下面的示例中,message类型为Set,点击Button改变message的值,视图会随之刷新。

@Entry
@Component
struct SetSample {
@State message: Set = new Set([0, 1, 2, 3, 4])

build() {
Row() {
Column() {
ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
Text(${item[0]}).fontSize(30)
Divider()
})
Button(‘init set’).onClick(() => {
this.message = new Set([0, 1, 2, 3, 4])
})
Button(‘set new one’).onClick(() => {
this.message.add(5)
})
Button(‘clear’).onClick(() => {
this.message.clear()
})
Button(‘delete the first one’).onClick(() => {
this.message.delete(0)
})
}
.width(‘100%’)
}
.height(‘100%’)
}
}

State支持联合类型实例

@State支持联合类型和undefined和null,在下面的示例中,count类型为number | undefined,点击Button改变count的属性或者类型,视图会随之刷新。

@Entry
@Component
struct EntryComponent {
build() {
Column() {
MyComponent()
}
}
}

@Component
struct MyComponent {
@State count: number | undefined = 0;

build() {
Column() {
Text(count(${this.count}))
Button(‘change’)
.onClick(() => {
this.count = undefined;
})
}
}
}

常见问题

使用箭头函数改变状态变量未生效

箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。所以在该场景下, changeCoverUrl的this指向PlayDetailViewModel,而不是被装饰器@State代理的状态变量。

反例:

export default class PlayDetailViewModel {
coverUrl: string = ‘#00ff00’

changeCoverUrl= ()=> {
this.coverUrl = ‘#00F5FF’
}

}


import PlayDetailViewModel from ‘./PlayDetailViewModel’

@Entry
@Component
struct PlayDetailPage {
@State vm: PlayDetailViewModel = new PlayDetailViewModel()

build() {
Stack() {
Text(this.vm.coverUrl).width(100).height(100).backgroundColor(this.vm.coverUrl)
Row() {
Button(‘点击改变颜色’)
.onClick(() => {
this.vm.changeCoverUrl()
})
}
}
.width(‘100%’)
.height(‘100%’)
.alignContent(Alignment.Top)
}
}

所以要将当前this.vm传入,调用代理状态变量的属性赋值。

正例:

export default class PlayDetailViewModel {
coverUrl: string = ‘#00ff00’

changeCoverUrl= (model:PlayDetailViewModel)=> {
model.coverUrl = ‘#00F5FF’
}

}


import PlayDetailViewModel from ‘./PlayDetailViewModel’

@Entry
@Component
struct PlayDetailPage {
@State vm: PlayDetailViewModel = new PlayDetailViewModel()

build() {
Stack() {
Text(this.vm.coverUrl).width(100).height(100).backgroundColor(this.vm.coverUrl)
Row() {
Button(‘点击改变颜色’)
.onClick(() => {
let self = this.vm
this.vm.changeCoverUrl(self)
})
}
}
.width(‘100%’)
.height(‘100%’)
.alignContent(Alignment.Top)
}
}

状态变量的修改放在构造函数内未生效

在状态管理中,类会被一层“代理”进行包装。当在组件中改变该类的成员变量时,会被该代理进行拦截,在更改数据源中值的同时,也会将变化通知给绑定的组件,从而实现观测变化与触发刷新。当开发者把状态变量的修改放在构造函数里时,此修改不会经过代理(因为是直接对数据源中的值进行修改),即使修改成功执行,也无法观测UI的刷新。

【反例】

@Entry
@Component
struct Index {
@State viewModel: TestModel = new TestModel();

build() {
Row() {
Column() {
Text(this.viewModel.isSuccess ? ‘success’ : ‘failed’)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.viewModel.query()
})
}.width(‘100%’)
}.height(‘100%’)
}
}

export class TestModel {
isSuccess: boolean = false
model: Model

constructor() {
this.model = new Model(() => {
this.isSuccess = true
console.log(this.isSuccess: ${this.isSuccess})
})
}

query() {
this.model.query()
}
}

export class Model {
callback: () => void

constructor(cb: () => void) {
this.callback = cb
}

query() {
this.callback()
}
}

上文示例代码将状态变量的修改放在构造函数内,界面开始时显示“failed”,点击后日志打印“this.isSuccess: true”说明修改成功,但界面依旧显示“failed”,未实现刷新。

【正例】

@Entry
@Component
struct Index {
@State viewModel: TestModel = new TestModel();

build() {
Row() {
Column() {
Text(this.viewModel.isSuccess ? ‘success’ : ‘failed’)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.viewModel.query()
})
}.width(‘100%’)
}.height(‘100%’)
}
}

export class TestModel {
isSuccess: boolean = false
model: Model = new Model(() => {
})

query() {
this.model = new Model(() => {
this.isSuccess = true
})
this.model.query()
}
}

export class Model {
callback: () => void

constructor(cb: () => void) {
this.callback = cb
}

query() {
this.callback()
}
}

上文示例代码将状态变量的修改放在类的普通方法中,界面开始时显示“failed”,点击后显示“success”。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

callback()
}
}

上文示例代码将状态变量的修改放在类的普通方法中,界面开始时显示“failed”,点击后显示“success”。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-JXxtd6bn-1713292974792)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值