HarmonyOS NEXT StateStore 状态管理实践

HarmonyOS NEXT StateStore 状态管理实践

##鸿蒙开发能力##HarmonyOS SDK应用服务##商务#

前言

在 HarmonyOS 应用开发中,多组件状态共享一直是复杂场景下的核心挑战。传统通过 @State+@Prop/@Link 或 @Provide+@Consume`实现的状态传递,会导致组件间状态耦合严重,尤其在多层级组件或兄弟组件共享状态时,代码维护成本急剧上升。

StateStore 作为 ArkUI 提供的状态管理解决方案,通过将状态逻辑与 UI 渲染解耦,实现了全局状态的集中管理与高效更新,为复杂应用的状态管理提供了优雅的解决方案。

StateStore 的核心价值在于:

1、状态与 UI 解耦:将状态管理逻辑从组件中抽离,组件仅负责视图渲染与事件分发

2、全局状态共享:支持跨组件、跨页面的状态统一管理,避免多层级状态传递

3、子线程状态更新:通过 SendableAction 机制实现子线程对全局状态的安全更新

4、中间件扩展能力:支持在状态更新流程中插入日志记录、权限校验等通用逻辑

StateStore 使用详解

一、StateStore 核心概念与原理
1、 架构组成

StateStore 基于 MVVM 模式设计,由以下核心部分构成:

(1)Store:状态管理仓库,存储全局状态并提供 ​​getState()​​​ 和 ​​dispatch(action)​​ 方法 (2)Reducer:状态更新逻辑函数,根据 Action 类型处理状态变更 (3)Action:事件描述对象,包含 ​​type​​​(事件类型)和 ​​payload​​(参数数据) (4)View:视图层组件,通过 ​​dispatch​​ 分发 Action 触发状态更新 (5)Middleware:中间件,在 Action 分发到 Reducer 前后插入自定义逻辑

2. 运行原理

StateStore 运行流程遵循以下机制: 1、View 层通过 ​​dispatch​​​ 发送 Action 到 Store 2、 Store 接收 Action 并调用对应的 Reducer 处理状态更新 3、 状态更新后,借助 ​​​@Observed/@ObservedV2​​ 的监听能力触发 UI 刷新 4、中间件可在状态更新前后执行额外逻辑(如日志埋点)

graph TD
    A[View 分发 Action] --> B[Store 接收 Action]
    B --> C[Reducer 处理状态更新]
    C --> D[状态变更]
    D --> E[UI 自动刷新]
    B --> M[Middleware 前置处理]
    C --> N[Middleware 后置处理]
二、基础使用流程
1. 定义业务数据模型

使用 @Observed/@ObservedV2`装饰数据类,实现状态变化的自动监听:

// TodoListModel.ets
@ObservedV2
export class TodoStoreModel {
  @Trace todoList: TodoItemData[] = [];
  @Trace isShow: boolean = false;
  addTaskTextInputValue: string = '';
  
  @Computed
  get uncompletedTodoList(): TodoItemData[] {
    return this.todoList.filter(item => !item.selected);
  }
}

@ObservedV2
export class TodoItemData {
  id: number = 0;
  @Trace taskDetail: string = '';
  @Trace selected?: boolean;
  
  constructor(taskDetail: string, selected?: boolean, id?: number) {
    this.id = id ? id : Date.now();
    this.taskDetail = taskDetail;
    this.selected = selected;
  }
}
2. 定义状态更新事件

通过 StateStore.createAction定义标准化 Action 类型:

// TodoListActions.ets
export default class TodoListActions {
  static getTodoList: Action = StateStore.createAction('getTodoList');
  static addTodoList: Action = StateStore.createAction('addTodoList');
  static deleteTodoItem: Action = StateStore.createAction('deleteTodoItem');
  static updateTaskDetail: Action = StateStore.createAction('updateTaskDetail');
}
3. 实现 Reducer 逻辑

定义状态更新的具体逻辑,根据 Action 类型处理数据变更:

// TodoListReducer.ets
export const todoReducer: Reducer<TodoStoreModel> = (state: TodoStoreModel, action: Action) => {
  switch (action.type) {
    case TodoListActions.getTodoList.type:
      return async () => {
        state.todoList = await queryFromDatabase(); // 模拟从数据库获取数据
      };
    case TodoListActions.addTodoList.type:
      if (state.addTaskTextInputValue === '') return null;
      state.todoList.push(new TodoItemData(state.addTaskTextInputValue));
      state.isShow = false;
      state.addTaskTextInputValue = '';
      break;
    // 其他 Action 处理逻辑...
  }
  return null;
};
4. 创建状态管理仓库

通过 StateStore.createStore初始化 Store,绑定数据模型与 Reducer:

// TodoListStore.ets
export const TODO_LIST_STORE_ID = 'todoListStore';

export const TodoStore: Store<TodoStoreModel> =
  StateStore.createStore(
    TODO_LIST_STORE_ID,
    new TodoStoreModel(),
    todoReducer,
    [LogMiddleware] // 注册中间件
  );
5. 在 UI 中使用状态

组件通过 getState()获取状态,并通过 dispatch()触发更新:

// Index.ets
@Entry
@ComponentV2
struct Index {
  @Local viewModel: TodoStoreModel = TodoStore.getState();
  
  aboutToAppear(): void {
    // 初始化时获取数据
    TodoStore.dispatch(TodoListActions.getTodoList);
  }
  
  build() {
    Column() {
      // 输入框绑定状态
      TextInput({ placeholder: '输入待办事项' })
        .value(this.viewModel.addTaskTextInputValue)
        .onChange((value) => {
          this.viewModel.addTaskTextInputValue = value;
        })
      
      // 列表渲染状态数据
      List() {
        ForEach(this.viewModel.uncompletedTodoList, (item) => {
          ListItem() {
            Row() {
              Checkbox({ select: item.selected })
                .onChange((value) => {
                  // 子组件通过 dispatch 触发状态更新
                  TodoStore.dispatch(
                    TodoListActions.completeTodoItem.setPayload({ id: item.id, value })
                  );
                })
              Text(item.taskDetail).fontSize(16);
            }
          }
        })
      }
    }
  }
}
三、高级功能实现
1. 子线程状态同步

通过 SendableAction实现子线程安全更新状态:

// 定义可发送的数据模型
@Sendable
export class ToDoItemSendable implements lang.ISendable {
  id: number;
  detail: string;
  selected: boolean;
  
  constructor(id: number, detail: string, selected: boolean = false) {
    this.id = id;
    this.selected = selected;
    this.detail = detail;
  }
}

// 子线程任务函数
@Concurrent
async function syncDatabase(context: Context, data: ToDoItemSendable[]): Promise<void> {
  // 模拟数据库操作
  for (const item of data) {
    // 子线程中发送 SendableAction
    taskpool.Task.sendData(
      StateStore.createSendableAction(
        TODO_LIST_STORE_ID,
        TodoListActions.updateProgress.type,
        item.id
      )
    );
    await sleep(500);
  }
}

// 主线程调用子线程任务
export async function handleSync() {
  const todos = TodoStore.getState().todoList;
  const sendableData = todos.map(item => new ToDoItemSendable(item.id, item.taskDetail, item.selected));
  
  const task = new taskpool.Task(syncDatabase, getContext(this), sendableData);
  task.onReceiveData((data: SendableAction) => {
    // 主线程接收并处理子线程发送的 Action
    StateStore.receiveSendableAction(data);
  });
  
  await taskpool.execute(task);
}
2. 中间件扩展

通过中间件实现状态更新日志埋点:

// 定义中间件
export class MiddlewareInstance<T> extends Middleware<T> {
  beforeAction: MiddlewareFuncType<T>;
  afterAction: MiddlewareFuncType<T>;
  
  constructor(before: MiddlewareFuncType<T>, after: MiddlewareFuncType<T>) {
    super();
    this.beforeAction = before;
    this.afterAction = after;
  }
}

// 日志中间件实现
export const LogMiddleware = new MiddlewareInstance<TodoStoreModel>(
  (state, action) => {
    hilog.info(0x0000, 'StateStore', `[BEFORE] Action: ${action.type}, State: ${JSON.stringify(state.todoList)}`);
    return MiddlewareStatus.NEXT;
  },
  (state) => {
    hilog.info(0x0000, 'StateStore', `[AFTER] Updated State: ${JSON.stringify(state.todoList)}`);
    return MiddlewareStatus.NEXT;
  }
);

// 在创建 Store 时注册中间件
export const TodoStore: Store<TodoStoreModel> =
  StateStore.createStore(TODO_LIST_STORE_ID, new TodoStoreModel(), todoReducer, [LogMiddleware]);

DEMO 源码

一、完整状态管理模型
// TodoListModel.ets
import { ObservedV2, Trace, Computed } from '@ohos.app.ability';

@ObservedV2
export class TodoItemData {
  id: number = 0;
  @Trace taskDetail: string = '';
  @Trace selected?: boolean;
  @Trace state: number = 0; // 0: 未处理, 1: 已处理
  
  constructor(taskDetail: string, selected: boolean = false, id?: number) {
    this.id = id ? id : Date.now();
    this.taskDetail = taskDetail;
    this.selected = selected;
  }
  
  toDoItemSendable(): ToDoItemSendable {
    return new ToDoItemSendable(this.id, this.taskDetail, this.selected);
  }
  
  updateState(state: number): void {
    this.state = state;
  }
}

@Sendable
export class ToDoItemSendable implements lang.ISendable {
  id: number;
  detail: string;
  selected: boolean;
  state: number;
  
  constructor(id: number, detail: string, selected: boolean = false) {
    this.id = id;
    this.selected = selected;
    this.detail = detail;
    this.state = 0;
  }
}

@ObservedV2
export class TodoStoreModel {
  @Trace todoList: TodoItemData[] = [];
  @Trace isShow: boolean = false;
  addTaskTextInputValue: string = '';
  @Trace progress: { value: number, total: number } = { value: 0, total: 0 };
  @Trace syncTodoList: TodoItemData[] = [];
  
  @Computed
  get uncompletedTodoList(): TodoItemData[] {
    return this.todoList.filter(item => !item.selected);
  }
  
  @Computed
  get completedTodoList(): TodoItemData[] {
    return this.todoList.filter(item => item.selected);
  }
}
二、状态更新事件与 Reducer
// TodoListActions.ets
import { Action } from '@ohos.app.ability';

export default class TodoListActions {
  static getTodoList: Action = StateStore.createAction('getTodoList');
  static addTodoList: Action = StateStore.createAction('addTodoList');
  static deleteTodoItem: Action = StateStore.createAction('deleteTodoItem');
  static updateTaskDetail: Action = StateStore.createAction('updateTaskDetail');
  static completeTodoItem: Action = StateStore.createAction('completeTodoItem');
  static setTotal: Action = StateStore.createAction('setTotal');
  static updateProgress: Action = StateStore.createAction('updateProgress');
  static clearProgress: Action = StateStore.createAction('clearProgress');
}

// TodoListReducer.ets
import { Reducer } from '@ohos.app.ability';
import { TodoStoreModel } from './TodoListModel';
import { TodoListActions } from './TodoListActions';

export const todoReducer: Reducer<TodoStoreModel> = (state: TodoStoreModel, action: Action) => {
  switch (action.type) {
    case TodoListActions.getTodoList.type:
      return async () => {
        // 模拟从数据库获取数据
        state.todoList = [
          new TodoItemData('完成文档编写', false),
          new TodoItemData('测试应用功能', false),
          new TodoItemData('优化性能问题', true)
        ];
      };
      
    case TodoListActions.addTodoList.type:
      if (state.addTaskTextInputValue === '') return null;
      state.todoList.push(new TodoItemData(state.addTaskTextInputValue));
      state.isShow = false;
      state.addTaskTextInputValue = '';
      break;
      
    case TodoListActions.deleteTodoItem.type:
      const idToDelete = action.payload?.id;
      if (idToDelete) {
        state.todoList = state.todoList.filter(item => item.id !== idToDelete);
      }
      break;
      
    case TodoListActions.completeTodoItem.type:
      const { id, value } = action.payload || {};
      const item = state.todoList.find(item => item.id === id);
      if (item) item.selected = value;
      break;
      
    case TodoListActions.setTotal.type:
      state.syncTodoList = state.todoList.filter(item => item.state === 0);
      state.progress.total = action.payload || 0;
      break;
      
    case TodoListActions.updateProgress.type:
      const itemId = action.payload;
      const syncItem = state.syncTodoList.find(item => item.id === itemId);
      syncItem?.updateState(1);
      state.progress.value++;
      break;
      
    case TodoListActions.clearProgress.type:
      state.progress = { value: 0, total: 0 };
      break;
  }
  return null;
};
三、Store 与中间件
// TodoListStore.ets
import { StateStore, Store, Middleware } from '@ohos.app.ability';
import { TodoStoreModel } from './TodoListModel';
import { todoReducer } from './TodoListReducer';
import { LogMiddleware } from './LoggerMiddleware';

export const TODO_LIST_STORE_ID = 'todoListStore';

export const TodoStore: Store<TodoStoreModel> =
  StateStore.createStore(
    TODO_LIST_STORE_ID,
    new TodoStoreModel(),
    todoReducer,
    [LogMiddleware]
  );

// LoggerMiddleware.ets
import { Middleware, MiddlewareFuncType, MiddlewareStatus } from '@ohos.app.ability';
import { TodoStoreModel } from './TodoListModel';

export class MiddlewareInstance<T> extends Middleware<T> {
  beforeAction: MiddlewareFuncType<T>;
  afterAction: MiddlewareFuncType<T>;

  constructor(beforeAction: MiddlewareFuncType<T>, afterAction: MiddlewareFuncType<T>) {
    super();
    this.beforeAction = beforeAction;
    this.afterAction = afterAction;
  }
}

export const LogMiddleware = new MiddlewareInstance<TodoStoreModel>(
  (state: TodoStoreModel, action: Action) => {
    console.log(`[MIDDLEWARE-BEFORE] Action: ${action.type}, State length: ${state.todoList.length}`);
    return MiddlewareStatus.NEXT;
  },
  (state: TodoStoreModel) => {
    console.log(`[MIDDLEWARE-AFTER] Updated State length: ${state.todoList.length}`);
    return MiddlewareStatus.NEXT;
  }
);
四、UI 组件示例
// Index.ets
import { ComponentV2, Entry, Local } from '@ohos.app.ability';
import { TodoStore, TODO_LIST_STORE_ID } from './model/TodoListStore';
import { TodoStoreModel } from './model/TodoListModel';
import { TodoListActions } from './model/TodoListActions';
import { TodoItem } from './components/TodoItem';
import { AddTodoDialog } from './components/AddTodoDialog';

@Entry
@ComponentV2
struct Index {
  @Local viewModel: TodoStoreModel = TodoStore.getState();
  @State isShowAddDialog: boolean = false;

  aboutToAppear(): void {
    TodoStore.dispatch(TodoListActions.getTodoList);
  }

  build() {
    Column() {
      // 标题栏
      Row() {
        Text('待办事项')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
        Spacer()
        Button('同步数据')
          .onClick(() => this.syncDatabase());
      }
      .width('100%')
      .padding(16)

      // 统计信息
      Row() {
        Text(`未完成: ${this.viewModel.uncompletedTodoList.length}`)
          .fontSize(14)
          .margin({ right: 20 })
        Text(`已完成: ${this.viewModel.completedTodoList.length}`)
          .fontSize(14)
      }
      .width('100%')
      .padding({ left: 16, bottom: 8 })
      .backgroundColor('#F5F5F5')

      // 待办列表
      if (this.viewModel.todoList.length > 0) {
        List({ space: 12 }) {
          if (this.viewModel.uncompletedTodoList.length > 0) {
            ListItemGroup({ header: this.todayGroupHeader(), space: 12 }) {
              ForEach(this.viewModel.uncompletedTodoList, (item: TodoItemData) => {
                ListItem() {
                  TodoItem({ itemData: item });
                }
              }, (item: TodoItemData) => item.id.toString());
            }
          }

          if (this.viewModel.completedTodoList.length > 0) {
            ListItemGroup({ header: this.completedGroupHeader(), space: 12 }) {
              ForEach(this.viewModel.completedTodoList, (item: TodoItemData) => {
                ListItem() {
                  TodoItem({ itemData: item, isCompleted: true });
                }
              }, (item: TodoItemData) => item.id.toString());
            }
          }
        }
        .width('100%')
        .layoutWeight(1)
      } else {
        Column() {
          Image($r('app.media.ic_todo_empty'))
            .width(120)
            .height(120)
            .margin({ top: 40 })
          Text('暂无待办事项,点击添加')
            .fontSize(16)
            .margin({ top: 20 })
            .textColor('#999999')
        }
        .width('100%')
        .layoutWeight(1)
      }

      // 添加按钮
      Button('+ 添加待办')
        .width(60)
        .height(60)
        .borderRadius(30)
        .backgroundColor('#0A74F1')
        .position({ x: this.viewModel.isShow ? 32 : 0, y: -32 })
        .onClick(() => {
          this.isShowAddDialog = true;
        })
    }
    .width('100%')
    .height('100%')
    .padding({ bottom: 20 })
    .bindDialog($$this.isShowAddDialog, this.addDialogBuilder())
  }

  // 分组标题
  @Builder
  todayGroupHeader() {
    Row() {
      Text('今日待办')
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
      Spacer()
      Text(`${this.viewModel.uncompletedTodoList.length} 项`)
        .fontSize(14)
        .textColor('#999999')
    }
    .padding({ top: 8, bottom: 8 })
  }

  @Builder
  completedGroupHeader() {
    Row() {
      Text('已完成')
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
      Spacer()
      Text(`${this.viewModel.completedTodoList.length} 项`)
        .fontSize(14)
        .textColor('#999999')
    }
    .padding({ top: 8, bottom: 8 })
  }

  @Builder
  addDialogBuilder() {
    AddTodoDialog({
      isShow: $isShowAddDialog,
      onConfirm: (text: string) => {
        TodoStore.dispatch(TodoListActions.addTodoList.setPayload({ text }));
      }
    })
  }

  async syncDatabase() {
    // 模拟子线程同步数据库
    const dialog = new ProgressDialog(this.context);
    dialog.show();
    
    try {
      await syncDatabase(); // 调用子线程同步函数
      TodoStore.dispatch(TodoListActions.getTodoList);
      this.viewModel = TodoStore.getState();
    } catch (error) {
      console.error(`Sync error: ${error}`);
    } finally {
      dialog.hide();
    }
  }
}
五、子线程同步工具
// TaskpoolUtil.ets
import { taskpool } from '@ohos.thread';
import { StateStore, SendableAction } from '@ohos.app.ability';
import { TodoListActions } from './model/TodoListActions';
import { TODO_LIST_STORE_ID } from './model/TodoListStore';
import { uiContext } from '@ohos.app.ability';
import { getContext } from '@ohos.app.ability';

@Concurrent
async function concurrentUpdateProgress(context: Object, data: ToDoItemSendable[]): Promise<void> {
  try {
    // 模拟数据库操作
    const total = data.length;
    taskpool.Task.sendData(
      StateStore.createSendableAction(TODO_LIST_STORE_ID, TodoListActions.setTotal.type, total)
    );
    
    for (const item of data) {
      // 模拟数据插入
      await sleep(300);
      taskpool.Task.sendData(
        StateStore.createSendableAction(TODO_LIST_STORE_ID, TodoListActions.updateProgress.type, item.id)
      );
    }
  } catch (err) {
    console.error(`${err.message}\n${err.stack}`);
    return undefined;
  }
}

export async function syncDatabase() {
  try {
    const todos: TodoItemData[] = TodoStore.getState().todoList;
    const ToBeSynced = todos.map(item => item.toDoItemSendable);
    
    const task = new taskpool.Task(concurrentUpdateProgress, getContext(this), ToBeSynced);
    task.onReceiveData((data: SendableAction) => {
      StateStore.receiveSendableAction(data);
    });
    
    await taskpool.execute(task);
    TodoStore.dispatch(TodoListActions.clearProgress);
  } catch (err) {
    console.error(`${err.message}\n${err.stack}`);
  }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值