为一篇讲解 HarmonyOS7 ArkTS 状态刷新问题的中文技术文章绘制一张 sketch-no

前言

状态改了,页面却没动。这类问题刚开始会让人怀疑 ArkUI 的刷新机制,其实大多数时候不是框架没刷新,而是代码没有给 UI 一次清楚的状态变化。

尤其是数组和对象,最容易踩坑:你改了对象里面的字段,但页面真正观察的是外层引用;你改了数组某一项,但没有把新数组赋回 @State

排查状态问题时,先找 UI 读了什么,再看事件到底改了什么。

为什么这个问题经常被写乱

ArkTS 状态刷新为什么没动静 这类内容很容易被写成“代码能跑就算讲完了”,但对初学者来说,这恰恰是最不够的地方。真正让人卡住的,往往不是某个组件名记不住,而是不知道这段代码为什么要这样拆、状态为什么要这样放、以后需求变化时应该从哪里改。

所以这篇文章不只想给你一个能跑的例子,更想把背后的判断过程讲清楚。你只要把这个判断过程吃透,后面自己改页面、补需求、查问题时,心里会稳很多。

为一篇讲解 HarmonyOS7 ArkTS 状态刷新为什么没动静的中文文章绘制一张 sketch-

状态刷新先看三件事

我一般按这个顺序排查:

排查点 典型问题 更稳的处理
UI 读的字段 文案读的是普通成员变量 放到 @State@Prop@Link 链路上
对象更新 只改 user.name 返回一个新对象再赋值
数组更新 只改 list[0] mapfilterconcat 生成新数组

不要一上来就怀疑组件。先沿着“用户点击 -> 状态变化 -> UI 读取”这条线走一遍,很多问题会很快露出来。

页面不会凭空知道你的业务变了。你要让被 UI 读取的状态发生明确变化。

先把页面目标想清楚

在真正写代码之前,先别急着盯着 API。更有用的做法是先想清楚:这个页面到底想解决什么问题,用户最在意的反馈是什么,哪些状态必须一直保持一致。

当你先把这条主线想明白,再回头看组件和状态设计,很多选择都会顺理成章。对小白来说,这一步尤其重要,因为它能帮你从“照着抄”慢慢过渡到“看得懂、改得动”。

完整 ArkUI 示例

为一篇关于 HarmonyOS7 ArkTS 状态刷新排查的中文技术文章绘制一张 sketch-no

下面这个计数面板故意用了数组和对象更新,适合观察状态刷新路径。

interface CounterTask {
  id: number
  title: string
  count: number
}

@Entry
@Component
struct StateRefreshPage {
  @State total: number = 0
  @State tasks: CounterTask[] = [
    { id: 1, title: '阅读文档', count: 0 },
    { id: 2, title: '运行示例', count: 0 }
  ]

  private addTaskCount(id: number): void {
    this.tasks = this.tasks.map((item: CounterTask) => {
      if (item.id === id) {
        return { id: item.id, title: item.title, count: item.count + 1 }
      }
      return item
    })
    this.total += 1
  }

  private reset(): void {
    this.tasks = this.tasks.map((item: CounterTask) => {
      return { id: item.id, title: item.title, count: 0 }
    })
    this.total = 0
  }

  build() {
    Column({ space: 14 }) {
      Text(`今日操作 ${this.total}`)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      ForEach(this.tasks, (item: CounterTask) => {
        Row() {
          Column({ space: 4 }) {
            Text(item.title).fontSize(16)
            Text(`已完成 ${item.count}`).fontSize(12).fontColor('#777777')
          }
          .alignItems(HorizontalAlign.Start)
          .layoutWeight(1)
          Button('+1')
            .height(32)
            .onClick(() => this.addTaskCount(item.id))
        }
        .padding(14)
        .backgroundColor(Color.White)
        .borderRadius(10)
      }, (item: CounterTask) => item.id.toString())

      Button('重置')
        .width('100%')
        .onClick(() => this.reset())
    }
    .padding(16)
    .backgroundColor('#F5F7FA')
  }
}

把关键代码一段段拆开

total 是顶部文案直接读取的状态,所以它放在 @State 里。每次点击 +1 都执行 this.total += 1,UI 能直接感知到变化。

tasks 是数组状态。点击某一项时,代码没有直接写 item.count += 1 后就结束,而是用 map 生成一个新数组,再赋值给 this.tasks。这条赋值语句很重要,它让页面看到“列表数据更新了”。

每个任务也返回了新对象:{ id, title, count: count + 1 }。这样做比原地改对象字段更清楚,后面排查问题时也更容易判断数据有没有变化。

reset() 同样没有原地循环修改,而是重新生成所有任务对象。写法稍微长一点,但刷新路径更直观。

常见误区

有些同学习惯把临时变量写成普通成员字段,然后在 UI 里展示它。普通字段变化不一定会触发页面刷新,如果它决定界面显示,就应该进入状态链路。

还有一种情况是状态重复存了两份:数组里有每项的 count,外面又存一个当前项对象。两边更新不同步,页面就会出现一处变了、一处没变。能从已有状态算出来的值,尽量用方法算,不要再存一份。

新手最容易踩的坑

这一类示例最容易让人产生错觉:界面出来了,就以为已经掌握了。其实真正容易出问题的地方,通常都在效果之外,比如状态有没有收拢、失败后怎么兜底、以后要扩展时会不会牵一发动全身。

所以你练这篇内容时,别只看“现在能不能跑”,还要继续看“以后好不好改”。能把这个习惯养起来,你写出来的页面会比单纯照着示例拼出来的页面稳很多。

放进真实项目还要补什么

示例代码的重点是把核心思路讲明白,所以很多工程化细节会故意省掉。真正落到项目里时,你通常还要继续补接口联动、异常处理、边界保护、资源抽离,以及和其他页面状态之间的配合。

写在最后

比较稳的做法是分三步走:先把结构和职责立住,再把真实业务接进去,最后再优化视觉和交互体验。这样改出来的页面不只是“能演示”,而是真的更接近可以长期维护的业务代码。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐