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}`);
}
}

被折叠的 条评论
为什么被折叠?



