HarmonyOS 7 实战开发 02:用状态驱动复杂页面交互

上一篇把工作台的基础框架搭起来了,三栏布局能跑通,但都是静态展示。这篇开始加交互------左侧导航切换、中间列表筛选、右侧详情联动,这些看似简单的功能,做起来才发现状态管理才是真正的难点。

很多人刚接触 ArkUI 的时候,觉得 @State 就是个响应式变量,写起来和 Vue 差不多。真到了复杂页面,组件层级一多,状态传着传着就乱了,改一个地方要找半天。这篇就讲讲我们项目里实际踩坑后总结出来的状态管理思路。

问题:组件通信到底该怎么传

工作台的三栏布局涉及三个组件:NavSideBar、ContentList、DetailPanel。用户在左侧点导航,中间列表要过滤;点中间列表项,右侧详情要更新。这三个组件是平级的,谁也不拥有谁。

一开始我想用 @Prop 一层一层往下传,传了两层就发现不对------每多一层嵌套就要写一遍 @Prop,代码冗余不说,改个字段名要改好几个文件。后来改成了状态提升,把数据放在最顶层的 WorkbenchPage 里,通过回调函数往下传。

状态提升的实际写法

核心思路很简单:把共享状态放在父组件,子组件通过 @Prop 接收数据,通过回调函数通知父组件状态变化。

下面是 WorkbenchPage 的状态管理代码,文件位置在 pages/WorkbenchPage.ets

typescript 复制代码
@Entry
@Component
struct WorkbenchPage {
  // 所有共享状态都放在这里
  @State selectedNavId: number = 0;
  @State selectedContent: ContentItem | null = null;
  @State isLoading: boolean = false;
  @State errorMsg: string = '';

  build() {
    Row() {
      // 左侧导航:传入当前选中项,回调通知父组件
      NavSideBar({
        selectedId: this.selectedNavId,
        onSelect: (id: number) => {
          this.selectedNavId = id;
          this.loadContentList(id);
        }
      })
      .width(200)

      // 中间列表:传入过滤后的数据和加载状态
      ContentList({
        items: this.filteredItems,
        isLoading: this.isLoading,
        errorMsg: this.errorMsg,
        onItemClick: (item: ContentItem) => {
          this.selectedContent = item;
        }
      })
      .layoutWeight(1)

      // 右侧详情:传入当前选中的内容
      DetailPanel({
        content: this.selectedContent
      })
      .width(360)
    }
    .width('100%')
    .height('100%')
  }

  // 根据选中的导航过滤列表
  private get filteredItems(): ContentItem[] {
    if (this.selectedNavId === 0) {
      return this.allItems;
    }
    return this.allItems.filter(item => item.category === this.selectedNavId);
  }

  private loadContentList(navId: number) {
    this.isLoading = true;
    this.errorMsg = '';
    // 模拟网络请求
    setTimeout(() => {
      this.isLoading = false;
    }, 500);
  }
}

这里的关键点:所有会影响多个组件的状态,都放在最顶层。子组件只负责展示和发事件,不自己存业务数据。这样不管组件层级多深,改状态只需要改父组件的地方。

列表筛选与选中态

中间列表的筛选逻辑其实不复杂,就是根据选中的导航 ID 过滤数据。但有个细节:列表项的选中态要跟着右侧详情联动------点了列表项,右侧详情更新,同时列表项要高亮。

ContentList 组件里的列表项代码,文件位置在 components/ContentList.ets

typescript 复制代码
@Component
export struct ContentList {
  @Prop items: ContentItem[];
  @Prop isLoading: boolean;
  @Prop errorMsg: string;
  onItemClick: (item: ContentItem) => void = () => {};

  build() {
    if (this.isLoading) {
      // 加载状态
      Column() {
        LoadingProgress()
          .width(40)
          .height(40)
      }
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)
    } else if (this.errorMsg) {
      // 错误状态
      Column() {
        Text(this.errorMsg)
          .fontSize(14)
          .fontColor('#ff4444')
        Button('重试')
          .margin({ top: 12 })
          .onClick(() => {
            // 重试逻辑
          })
      }
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)
    } else {
      // 正常列表
      List({ space: 8 }) {
        ForEach(this.items, (item: ContentItem) => {
          ListItem() {
            ContentCard({ item: item })
          }
        }, (item: ContentItem) => item.id)
      }
      .layoutWeight(1)
      .padding(12)
    }
  }
}

这里把三种状态------加载中、错误、正常------都在同一个组件里处理了。父组件只需要传 isLoading 和 errorMsg,子组件自动切换显示对应的 UI。这比在父组件里写一堆 if-else 干净多了。

详情联动的细节

右侧详情面板要响应选中项的变化。一开始我直接在 DetailPanel 的 aboutToAppear 里取数据,结果发现切换列表项的时候,详情面板不更新------因为 aboutToAppear 只在组件首次创建时调用一次。

正确的做法是:DetailPanel 用 @Prop 接收 content,content 变了,UI 自动刷新。不需要手动监听,ArkUI 的响应式系统会处理。

还有个容易踩的坑:@Prop 是单向绑定,父组件改了会同步到子组件,但子组件改了不会影响父组件。如果需要双向同步,得用 @Link。这个场景里详情面板只读数据,用 @Prop 就够了。

状态管理的经验总结

做了这个页面之后,总结了几条经验:

1. 状态尽量往上提:两个以上组件共享的状态,放在它们共同的父组件里。不要在子组件里自己存一份,然后还要同步,容易出 bug。

2. 子组件要纯:子组件尽量只接收 props 和回调,不维护业务状态。这样组件复用性好,测试也方便。

3. 异常状态要单独处理:加载中、空数据、错误,这三种状态不要混在正常列表里写。单独拆出来,UI 更清晰。

4. 别滥用 @Link:@Link 虽然方便,但用多了数据流向就乱了,搞不清谁改了值。大部分场景 @Prop + 回调就够了。

小结

这篇讲了怎么用状态提升把三栏页面的交互串起来。看起来都是基础 API,但真到了复杂页面,状态管理的思路比 API 本身更重要。下一篇会讲列表性能优化,数据量大了之后怎么保证流畅度。

相关推荐
大雷神1 小时前
【共创稿事节】HarmonyOS ArkGraphics 3D 实操——给智能音箱做一台结构探索台
harmonyos
李游Leo2 小时前
HarmonyOS 7 性能优化:List 长列表懒加载与缓存实践
harmonyos
威哥爱编程2 小时前
HarmonyOS 碰一碰与隔空传送实战:真正的坑在 3 秒铁律和生命周期
harmonyos
威哥爱编程2 小时前
HarmonyOS 扫码直达接入实战:系统扫、应用落,三步送用户进履约页
华为·harmonyos·arkts
威哥爱编程3 小时前
HarmonyOS 6.0 智感握姿实战:一道安检门、五态分诊、一静一响两个坑
华为·harmonyos·arkts
李游Leo3 小时前
HarmonyOS 7 音频与媒体控制实战 02:实现系统音频内录与状态控制
harmonyos
李游Leo3 小时前
HarmonyOS 7 音频与媒体控制实战 05:排查播放无声、卡顿和杂音问题
harmonyos
大雷神3 小时前
【共创稿事节】HarmonyOS ArkGraphics 3D 实操:用 GLB 节点与 PBR 材质打造智能音箱选配器
harmonyos
贾伟康12 小时前
【HarmonyOS 7新能力|020】LazyLayoutAlgorithm入门实战:从能力边界到最小可运行链路
harmonyos·arkts·arkui·harmonyos 7·lazylayout