HarmonyOS鸿蒙Next中@State修饰的变量,变化后UI不更新
HarmonyOS鸿蒙Next中@State修饰的变量,变化后UI不更新 父组件 HomeCategory,子组件 QuestionItemListComp
父组件中调用子组件,同时向子组件传参,子组件的aboutToApper方法中,会根据这个参数获取请求结果。我通过log日志打印,发现请求结果是能获取到的,但是为什么UI不更新
import { PageData, QuestionItem } from "../../models"
import { promptAction } from "@kit.ArkUI"
import { http } from "../utils/Http"
import { JSON } from "@kit.ArkTS"
import { logger } from "../utils/Logger"
import json from "@ohos.util.json"
import { QuestionItemListComp2 } from "./QuestionItemListComp2"
interface QuestionType {
name: string,
displayNewestFlag: 1|0,
id: number
}
interface QuestionItemType{
text:string,
color:Resource
}
const diffColorText:Record<number,QuestionItemType>={
1:{text:'简单',color:$r('app.color.common_green')},
2:{text:'简单',color:$r('app.color.common_green')},
3:{text:'一般',color:$r('app.color.common_blue')},
4:{text:'一般',color:$r('app.color.common_blue')},
5:{text:'困难',color:$r('app.color.common_main_color')}
}
interface QuestionItemListParam{
type?:string,
keyword?:string,
sort?:string,
pageSize?:string,
page?:string,
questionBankType:string
}
@Component
struct QuestionItemListComp {
[@State](/user/State)
questionItemList:QuestionItem[]=[]
@Prop
type:string ='401'
async getQuestionItemList(type:string,page:string,sort:string='0',pageSize:string='10',questionBankType:string='10'){
let result = await http.request<PageData<QuestionItem>>({
url:'test/question/list',
params:{
type, //QuestionItem的id
keyword:'', //保持为空
sort,
pageSize,
page,
questionBankType
} as QuestionItemListParam
})
// this.questionItemList.push(...result.rows)
return result
}
async aboutToAppear(){
const result = await this.getQuestionItemList(this.type,'1')
// this.questionItemList.push(...result.rows)
this.questionItemList = result.rows
logger.info(JSON.stringify(result.rows))
}
//问题条框Builder
@Builder
QuestionItemBuilder(questionItem:QuestionItem) {
Column({space:10}){
Row(){
Text(diffColorText[questionItem.difficulty].text)
Text(questionItem.stem)
}
RowSplit(){
Text(`点赞 ${questionItem.likeCount}`)
.padding({right:10})
Text(`浏览 ${questionItem.views}`)
.padding({left:10,right:10})
if (questionItem.readFlag==1){
Text('已看过')
.padding({left:10,right:10})
}
}
}
.margin({top:15,bottom:15})
.alignItems(HorizontalAlign.Start)
}
//存放问题条框的Builder
@Builder
QuestionItemListBuilder(questionItems:QuestionItem[]){
Refresh({refreshing:false}){
List(){
ForEach(questionItems,(item:QuestionItem,index:number)=>{
this.QuestionItemBuilder(item)
})
}
// .backgroundColor(Color.Green)
.divider({
strokeWidth:1,
color:'#ffc8c8c8'
})
.padding({left:10,right:10})
}
.height('100%')
.width('100%')
}
build() {
this.QuestionItemListBuilder(this.questionItemList)
}
}
@Component
@Preview
export struct HomeCategory {
[@State](/user/State)
type:string = '0'
[@State](/user/State)
questionTypeList: QuestionType[] = []
[@State](/user/State)
activeIndex: number = 0
[@State](/user/State)
freshFlag:boolean = false
[@State](/user/State)
questionItemList:QuestionItem[]=[]
async getQuestionTypeList(){
let result = await http.request<QuestionType[]>({
url:'hm/question/type'
})
this.questionTypeList = result
}
//顶部问题分类builder
@Builder
QuestionTypeBuilder(questionType: QuestionType,index:number) {
Row() {
Text(questionType.name) //问题类型
.margin({right:4})
if (questionType.displayNewestFlag==1) { //如果是true,就显示图片new
Image($r('app.media.ic_home_new'))
.width(40)
}
}
.padding({ left: 5, right: 5 })
// .border({
// width: { bottom: this.activeIndex==questionType.id?1:0 },
// color: $r('app.color.black')
// })
.backgroundColor(this.activeIndex==index? '#ffe5e5e5' :'#00000000')
.borderRadius('50%')
}
aboutToAppear(): void {
this.getQuestionTypeList()
}
build() {
Tabs() {
ForEach(this.questionTypeList, (item: QuestionType, index: number) => {
TabContent() {
QuestionItemListComp({type:item.id.toString()})
}.tabBar(this.QuestionTypeBuilder(item,index))
})
}
.barMode(BarMode.Scrollable)
.backgroundColor(Color.White)
.barPosition(BarPosition.Start)
.barHeight(40)
.divider({ strokeWidth: 1, color: $r('app.color.common_gray_01') })
// .onChange((index)=>{
// this.activeIndex = index
// })
.onTabBarClick((index:number)=>{
this.activeIndex = index
})
// .height(60)
}
}
更多关于HarmonyOS鸿蒙Next中@State修饰的变量,变化后UI不更新的实战教程也可以访问 https://www.itying.com/category-93-b0.html
原因在于响应式只跟踪“引用地址变化”。你在子组件里更新的是数组里“同一引用对象”的内容或把服务端返回的“数据”直接赋给 questionItemList(引用未变),因此 UI 不刷新。
建议(任意其一即可触发更新):
- 重新创建新引用再赋值:例如将结果转成全新数组(如浅拷贝或构造新模型),再赋给
questionItemList。 - 元素级替换也要保持新引用:对每个元素用“新对象”替换旧对象。
- 若是对象模型,优先用 V2 可观察模型(@ObservedV2 + @Trace)配合组件(@ComponentV2),确保深度属性变更可被跟踪。
更多关于HarmonyOS鸿蒙Next中@State修饰的变量,变化后UI不更新的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
问题描述: 子组件对象questionItemList:QuestionItem[]=[]已经赋值了,并且有数据了,但是没有更新到UI组件,数据没有刷新。
解决方案: 从网络服务端获取到的数据源对象,需要进行new才会刷新UI
详细步骤:
async aboutToAppear() {
const result = await this.getQuestionItemList(this.type,'1')
result.forEach(item =>{
this.questionItemList.push(new QuestionItem());
})
}
希望对您有帮助,望采纳!
- 直接使用ForEach替代@Builder
避免通过@Builder传递状态变量,直接在build方法中使用ForEach绑定@State变量:
@Component
struct QuestionItemListComp {
[@State](/user/State) questionItemList: QuestionItem[] = [];
build() {
Refresh({ refreshing: false }) {
List() {
ForEach(this.questionItemList,
(item: QuestionItem) => {
ListItem() {
// 直接在此处构建UI项
Column({ space: 10 }) {
Row() {
Text(diffColorText[item.difficulty].text)
Text(item.stem)
}
// ... 其他UI内容
}
}
},
(item: QuestionItem) => item.id.toString() // 提供键值生成函数
)
}
}
}
}
2. 6.0(API20)使用@Builder响应式支持
参考 [@Builder支持状态变量刷新](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builder#builder支持状态变量刷新)
从API version 20开始,开发者可以通过使用UIUtils.makeBinding()函数、Binding类和MutableBinding类实现@Builder函数中状态变量的刷新。详情请参考状态管理API文档。
import { Binding, MutableBinding, UIUtils } from '@kit.ArkUI';
@ObservedV2
class ClassA {
@Trace props: string = 'Hello';
}
[@Builder](/user/Builder)
function CustomButton(num1: Binding<number>, num2: MutableBinding<number>) {
Row() {
Column() {
Text(`number1 === ${num1.value}, number2 === ${num2.value}`)
.width(300)
.height(40)
.margin(10)
.backgroundColor('#0d000000')
.fontColor('#e6000000')
.borderRadius(20)
.textAlign(TextAlign.Center)
Button(`only change number2`)
.onClick(() => {
num2.value += 1;
})
}
}
}
[@Builder](/user/Builder)
function CustomButtonObj(obj1: MutableBinding<ClassA>) {
Row() {
Column() {
Text(`props === ${obj1.value.props}`)
.width(300)
.height(40)
.margin(10)
.backgroundColor('#0d000000')
.fontColor('#e6000000')
.borderRadius(20)
.textAlign(TextAlign.Center)
Button(`change props`)
.onClick(() => {
obj1.value.props += 'Hi';
})
}
}
}
@Entry
@ComponentV2
struct Single {
@Local number1: number = 5;
@Local number2: number = 12;
@Local classA: ClassA = new ClassA();
build() {
Column() {
Button(`change both number1 and number2`)
.onClick(() => {
this.number1 += 1;
this.number2 += 2;
})
Text(`number1 === ${this.number1}`)
.width(300)
.height(40)
.margin(10)
.backgroundColor('#0d000000')
.fontColor('#e6000000')
.borderRadius(20)
.textAlign(TextAlign.Center)
Text(`number2 === ${this.number2}`)
.width(300)
.height(40)
.margin(10)
.backgroundColor('#0d000000')
.fontColor('#e6000000')
.borderRadius(20)
.textAlign(TextAlign.Center)
CustomButton(
UIUtils.makeBinding<number>(() => this.number1),
UIUtils.makeBinding<number>(
() => this.number2,
(val: number) => {
this.number2 = val;
})
)
Text(`classA.props === ${this.classA.props}`)
.width(300)
.height(40)
.margin(10)
.backgroundColor('#0d000000')
.fontColor('#e6000000')
.borderRadius(20)
.textAlign(TextAlign.Center)
CustomButtonObj(
UIUtils.makeBinding<ClassA>(
() => this.classA,
(val: ClassA) => {
this.classA = val;
})
)
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.Center)
}
}

【背景知识】
@Prop装饰的变量可以和父组件建立单向同步关系。
子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。
在阅读@Prop文档前,建议开发者首先了解@State的基本用法。最佳实践请参考状态管理最佳实践。
【问题分析】
在V1的状态变量管理中涉及到父组件与子组件的状态变量交互的时候,需要使用@Prop或者@Link来修饰状态变量
【参考文档】
[@Link装饰器:父子双向同步-管理组件拥有的状态-状态管理(V1)-学习UI范式状态管理-UI开发 (ArkTS声明式开发范式)-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-link)

