及时做APP开发实战(十三)-已完成数量统计修复

及时做APP开发实战(十三)-已完成数量统计修复

本文将介绍如何修复主页面已完成数量不更新的BUG,并深入分析ArkTS状态管理机制。

一、问题分析

1.1 问题现象

图 1 问题现象-不及时更新已完成数量

复制代码
┌─────────────────────────────────────┐
│  待办列表                    🌙     │
├─────────────────────────────────────┤
│  ┌─────────────────────────────┐   │
│  │  进行中    已完成    全部    │   │
│  │    3        0❌      5      │   │  ← 已完成数量不更新
│  └─────────────────────────────┘   │
│                                     │
│  ☐ 学习ArkTS                        │
│  ☐ 开发及时做APP                    │
│  ✓ 写博客文档     ← 已完成          │
│  ✓ 代码优化       ← 已完成          │
│  ☐ 测试功能                         │
└─────────────────────────────────────┘

1.2 问题原因

复制代码
问题代码:
┌─────────────────────────────────────┐
│  Text(this.todoViewModel            │
│    .getCompletedTodoCount()         │
│    .toString())                     │
└─────────────────────────────────────┘
         ↓
  调用ViewModel的方法获取数据
         ↓
  ❌ 不是@State变量,UI不会自动刷新

二、ArkTS状态管理机制

2.1 状态变量装饰器

装饰器 作用 触发UI更新
@State 组件内状态 ✅ 自动更新
@Prop 父→子单向传递 ✅ 自动更新
@Link 父↔子双向绑定 ✅ 自动更新
@Provide 跨组件提供 ✅ 自动更新
@Consume 跨组件消费 ✅ 自动更新

2.2 为什么ViewModel方法不触发更新

typescript 复制代码
// ❌ 问题代码
@Component
struct Index {
  @State todoList: TodoItem[] = [];
  private todoViewModel: TodoViewModel = new TodoViewModel();
  
  build() {
    Column() {
      // 调用ViewModel方法
      Text(this.todoViewModel.getCompletedTodoCount().toString())
      // ↑ 不是@State变量,值变化时UI不更新
    }
  }
}

// ✅ 正确代码
@Component
struct Index {
  @State todoList: TodoItem[] = [];
  
  build() {
    Column() {
      // 基于@State变量计算
      Text(this.todoList.filter(item => item.completed).length.toString())
      // ↑ 依赖@State变量,值变化时UI自动更新
    }
  }
}

2.3 状态更新流程

复制代码
┌─────────────────────────────────────┐
│          状态更新流程                │
├─────────────────────────────────────┤
│  1. @State变量被修改                 │
│      ↓                              │
│  2. ArkTS检测到变化                  │
│      ↓                              │
│  3. 触发组件重新渲染                  │
│      ↓                              │
│  4. 重新计算依赖该变量的表达式        │
│      ↓                              │
│  5. UI更新显示                       │
└─────────────────────────────────────┘

三、解决方案

3.1 修改前代码

typescript 复制代码
// 统计信息组件
@Builder
StatisticsBar() {
  Row() {
    Column() {
      // ❌ 调用ViewModel方法
      Text(this.todoViewModel.getActiveTodoCount().toString())
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FF6B6B')
      Text('进行中')
        .fontSize(12)
        .fontColor('#666666')
    }
    .layoutWeight(1)
    
    Column() {
      // ❌ 调用ViewModel方法
      Text(this.todoViewModel.getCompletedTodoCount().toString())
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#4CAF50')
      Text('已完成')
        .fontSize(12)
        .fontColor('#666666')
    }
    .layoutWeight(1)
    
    Column() {
      // ✅ 基于@State变量
      Text(this.todoList.length.toString())
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#2196F3')
      Text('全部')
        .fontSize(12)
        .fontColor('#666666')
    }
    .layoutWeight(1)
  }
}

3.2 修改后代码

typescript 复制代码
// 统计信息组件
@Builder
StatisticsBar() {
  Row() {
    Column() {
      // ✅ 基于@State变量计算
      Text(this.todoList.filter(item => !item.completed).length.toString())
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FF6B6B')
      Text('进行中')
        .fontSize(12)
        .fontColor('#666666')
    }
    .layoutWeight(1)
    
    Column() {
      // ✅ 基于@State变量计算
      Text(this.todoList.filter(item => item.completed).length.toString())
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#4CAF50')
      Text('已完成')
        .fontSize(12)
        .fontColor('#666666')
    }
    .layoutWeight(1)
    
    Column() {
      // ✅ 基于@State变量
      Text(this.todoList.length.toString())
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#2196F3')
      Text('全部')
        .fontSize(12)
        .fontColor('#666666')
    }
    .layoutWeight(1)
  }
}

3.3 关键改动

修改项 修改前 修改后
进行中数量 todoViewModel.getActiveTodoCount() todoList.filter(item => !item.completed).length
已完成数量 todoViewModel.getCompletedTodoCount() todoList.filter(item => item.completed).length
全部数量 todoList.length todoList.length(不变)

四、验证修复

4.1 测试步骤

复制代码
1. 启动应用,进入主页面
   ↓
2. 查看统计栏显示:进行中 X,已完成 0,全部 X
   ↓
3. 点击待办项,标记为已完成
   ↓
4. 观察统计栏:已完成数量应该 +1
   ↓
5. 再次点击,取消完成状态
   ↓
6. 观察统计栏:已完成数量应该 -1

4.2 预期结果

复制代码
┌─────────────────────────────────────┐
│          统计栏正确更新              │
├─────────────────────────────────────┤
│  操作前:进行中 3,已完成 0,全部 3   │
│  标记完成:进行中 2,已完成 1,全部 3  │
│  再标记完成:进行中 1,已完成 2,全部 3 │
│  取消完成:进行中 2,已完成 1,全部 3  │
└─────────────────────────────────────┘

图 2 修复后效果

五、最佳实践总结

5.1 状态管理原则

复制代码
┌─────────────────────────────────────┐
│          ✅ 状态管理原则             │
├─────────────────────────────────────┤
│  □ UI显示的数据应依赖@State变量       │
│  □ 避免在build()中调用非状态方法      │
│  □ ViewModel方法用于业务逻辑,不直接用于UI显示 │
│  □ 使用计算属性/表达式基于@State计算   │
└─────────────────────────────────────┘

5.2 常见错误模式

typescript 复制代码
// ❌ 错误模式1:调用方法获取数据
Text(this.getData().toString())

// ❌ 错误模式2:访问非状态变量
Text(this.count.toString()) // count不是@State

// ✅ 正确模式:基于@State计算
Text(this.todoList.filter(...).length.toString())

六、总结

本文修复了主页面已完成数量不更新的BUG,核心要点:

  1. 问题原因:调用ViewModel方法不触发UI更新
  2. 解决方案:基于@State变量计算显示值
  3. 关键原理:ArkTS状态管理机制,只有@State变量变化才触发UI更新

相关推荐
绝世番茄8 小时前
鸿蒙原生 ArkTS 布局之道:Grid 替代多层嵌套布局深度解析
华为·harmonyos·鸿蒙
●VON8 小时前
鸿蒙 PC Markdown 编辑器可靠性设计:未保存内容的崩溃恢复闭环
华为·中间件·编辑器·harmonyos·鸿蒙
<小智>9 小时前
及时做APP开发实战(十五)-专注历史记录功能实现
鸿蒙
●VON9 小时前
鸿蒙 PC Markdown 编辑器导出架构:安全 HTML 与系统 PDF
华为·架构·编辑器·harmonyos·鸿蒙
条tiao条1 天前
Navigation页面跳转教学博客
ui·华为·harmonyos·鸿蒙·页面跳转
shaodong11231 天前
HarmonyOS新特性-沉浸光感在叠叠消小游戏中的落地实践
harmonyos·鸿蒙
xd1855785551 天前
歌词创作工坊-基于鸿蒙的AI歌词创作应用开发实践
人工智能·华为·harmonyos·鸿蒙
千逐681 天前
HarmonyOS 实战 | 弹窗/提示框怎么做——Alert、Dialog、Toast 详解
华为·harmonyos·鸿蒙
xd1855785551 天前
藏头诗工坊-基于鸿蒙的AI藏头诗创作应用开发实践
人工智能·学习·华为·harmonyos·鸿蒙