鸿蒙6.0应用开发——基于StateStore的全局状态管理
使用ArkUI开发页面时,多组件状态共享是我们经常会遇到的场景;ArkUI通过装饰器,例如@State+@Prop/@Link、@Provide+@Consume实现父子组件状态共享,但是这样会造成状态数据耦合。作为ArkUI状态与UI解耦的解决方案,支持全局维护状态,优雅地解决状态共享的问题。让开发者在开发过程中实现状态与UI解耦,多个组件可以方便地共享和更新全局状态,将状态管理逻辑从组件逻辑中
鸿蒙6.0应用开发——基于StateStore的全局状态管理
概述
使用ArkUI开发页面时,多组件状态共享是我们经常会遇到的场景;ArkUI通过装饰器,例如@State+@Prop/@Link、@Provide+@Consume实现父子组件状态共享,但是这样会造成状态数据耦合。
作为ArkUI状态与UI解耦的解决方案,支持全局维护状态,优雅地解决状态共享的问题。让开发者在开发过程中实现状态与UI解耦,多个组件可以方便地共享和更新全局状态,将状态管理逻辑从组件逻辑中分离出来,简化维护。
StateStore提供了下列功能特性:
- 状态对象与UI解耦,支持状态全局化操作。
- 支持在子线程中进行状态对象更新。
- 支持状态更新执行的预处理和后处理。
ArkUI状态管理现状
在多组件状态共享的场景中,我们常常遇到的问题是,当多个组件需要共享相同的状态时,必须通过它们的共同父组件来传递和维护这些状态数据。
例如,我们实现如下图待办列表的新增与删除功能。
图1 待办列表效果图

新增和删除功能按钮分别位于两个兄弟组件中。在开发时,父组件需要维护一个listDatas列表,并通过@Link装饰器实现数据的双向同步,从而实现兄弟组件之间的状态同步。删除功能和新增功能逻辑分别由两个子组件处理,但是这两个组件都需要引入与UI渲染无关的listDatas数据,造成了状态与UI的高耦合。使得状态管理变得复杂,难以维护和扩展。组件结构图如下:

引入StateStore库后,开发者可以将listDatas数据存储在全局仓库(Store)中,组件从Store中获取数据进行UI渲染,并通过向Store发送事件来更新数据。这样,状态更新逻辑被集中管理,组件无需额外引入状态进行逻辑处理,从而实现了状态与UI的低耦合。组件结构图如下:

实现原理
StateStore基于ArkUI的状态管理特性(@Observed和@ObservedV2)实现全局状态管理。统一由UI分发事件指令,状态管理仓库触发对应的状态更新逻辑,实现状态与UI解耦。
具体步骤如下
- @ObservedV2装饰类实现数据监听
- Store集中管理被观察对象
- 状态变更通过装饰器触发UI更新
图2 运行原理图

核心概念解释
-
View:视图层
View构成了用户界面,它包含了丰富的页面UI组件,并响应用户操作。当用户和UI进行交互时,View会通过dispatch方法分发Action事件,从而触发状态更新的流程。
-
Store:状态管理仓库
Store作为状态管理的核心,主要向外部提供两个关键方法:getState()和dispatch(action)。getState()方法允许外部获取当前的状态信息,而dispatch(action)方法则用于接收并处理来自UI的Action事件。
-
Reducer:状态刷新逻辑处理函数
Reducer是一个专门负责状态刷新逻辑的函数。它会根据传入的Action事件指令,对状态进行更新。每一个Action事件都携带着特定的指令,Reducer会根据这些指令来精确地修改状态。
-
Dispatch:事件分发方法
Dispatch是UI侧与Store进行交互的桥梁,也是UI侧触发状态更新的唯一途径。UI侧通过调用Dispatch方法,将封装了事件类型的Action对象发送到Store,从而触发后续的状态更新流程。
-
Action:事件描述对象
Action是一个用于描述指示发生了何种事件的对象。它包含了两个重要的属性:type和payload。type属性用于标识事件的类型,而payload属性则携带了事件相关的具体数据。通过这两个属性,Action能够完整地描述一个事件,并引导Reducer进行状态更新。
UI刷新原理
数据改变刷新UI的能力依赖系统侧@Observed/@ObservedV2对数据的观测能力,StateStore不接管数据驱动UI更新。
说明
开发步骤
-
定义业务数据
开发者使用@Observed或@ObservedV2修饰业务数据,并生成实例对象。
-
定义状态更新逻辑函数
开发者需要定义状态处理函数,该函数类型为Reducer。该函数负责根据业务逻辑来更新数据。
-
创建状态管理仓库
为了集中管理状态更新,开发者使用StateStore.createStore方法来创建一个状态管理仓库(即Store对象)。在调用createStore方法时,需要传入业务数据的对象实例和业务逻辑函数,以便为Store对象绑定相应的初始状态和Reducer。
-
组件UI的初始渲染
在组件内部,开发者通过调用Store对象的getState()方法来获取业务数据,并据此编写UI结构。这样,组件即可根据获取到的数据进行渲染。
-
组件UI的刷新
-
创建Action事件对象
这一步的目的是告诉store对象,你要做什么;例如,我们需要添加某个数据,则创建一个添加事件——AddAction。
-
UI触发事件
事件定义好后,需要某个操作来触发事件,即我们在UI中通过dispatch(AddAction)发送该添加事件给store,接收到事件后会通知实际处理者——Reducer,根据接收到的AddAction事件处理对应的添加逻辑,修改状态数据。
-
UI刷新
Reducer类型函数的逻辑被触发后,状态会随之更新。借助系统提供的@Observed或@ObservedV2装饰器的监听能力,UI能够与状态保持同步刷新。
-
使用StateStore实现状态与UI解耦
场景描述
在开发复杂应用时,状态与UI的强耦合常常导致代码臃肿、难以维护,尤其是在多个组件需要共享状态时,这种问题尤为突出。使用StateStore,开发者可以将状态管理逻辑完全从UI中抽离,实现状态的集中式管理和更新,进而简化代码结构、提高可维护性。
本节以备忘录应用为例,演示如何通过StateStore实现多个组件的状态共享与更新,同时保持UI层的纯粹性。
图3 效果图

开发步骤
-
定义页面数据
使用@ObservedV2定义页面需要的数据TodoList、TodoItem。
@ObservedV2 export class TodoStoreModel { @Trace todoList: TodoItemData[] = []; @Trace isShow: boolean = false; addTaskTextInputValue: string = ''; // ... @Computed get uncompletedTodoList(): TodoItemData[] { return this.todoList.filter(item => !item.selected); } @Computed get completedTodoList(): 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; // ... } // ... } -
创建状态管理仓库
-
定义状态更新事件类型Action
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'); // ... }; -
定义状态处理函数TodoReducer
export const todoReducer: Reducer<TodoStoreModel> = (state: TodoStoreModel, action: Action) => { let GlobalContent = GlobalContext.getInstance(); uiContext = GlobalContent.getUIContext() switch (action.type) { case TodoListActions.getTodoList.type: return async () => { state.todoList = (await RdbUtil.getInstance(uiContext?.getHostContext()!)).query(); }; case TodoListActions.addTodoList.type: if (state.addTaskTextInputValue === '') { uiContext!.getPromptAction().showToast({ message: $r('app.string.empty') }); return null; } state.todoList.push(new TodoItemData(state.addTaskTextInputValue)); state.isShow = false; state.addTaskTextInputValue = ''; break; case TodoListActions.deleteTodoItem.type: // ... break; case TodoListActions.updateTaskDetail.type: // ... break; case TodoListActions.completeTodoItem.type: // ... break; // ... } return null; }; -
创建状态管理仓库
export const TODO_LIST_STORE_ID = 'todoListStore'; export const TodoStore: Store<TodoStoreModel> = StateStore.createStore(TODO_LIST_STORE_ID, new TodoStoreModel(), todoReducer, [LogMiddleware]);
-
-
在UI中使用
- 通过getState()拿到Store中的状态数据。
- 使用dispatch()派发一个状态更新事件来刷新UI。
如下例子中:Index组件内,通过getState()方法获取状态数据并绑定UI,通过dispatch触发GetTodoList事件获取全量数据并更新状态;TodoItem子组件中通过dispatch方法派发一个CompleteTodoItem事件来改变全局状态,将当前项设置为已完成。
@Entry @ComponentV2 struct Index { @Local viewModel: TodoStoreModel = TodoStore.getState(); // ... aboutToAppear(): void { // The dispatch triggers a GetTodoList event to get the full data and update the status TodoStore.dispatch(TodoListActions.getTodoList); } // ... build() { Column() { // ... 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()); }; } // ... }.width('100%') .height('100%') .layoutWeight(1); // ... } }@ComponentV2 export struct TodoItem { @Param @Require itemData: TodoItemData; // ... build() { Row({ space: 8 }) { Checkbox({ name: 'checkbox1', group: 'checkboxGroup' }) .select(this.itemData.selected) .shape(CheckBoxShape.CIRCLE) .onChange((_value) => { // The child component changes the global state by sending a CompleteTodoItem event through the dispatch method, setting the current item to complete TodoStore.dispatch(TodoListActions.completeTodoItem.setPayload({ id: this.itemData.id, value: _value })); }); // ... } // ... } }
通过StateStore库的使用,在UI上就没有任何状态更新逻辑,UI层面只需要关注界面描述和事件分发,保持了UI层的纯粹性。UI界面通过事件触发dispatch操作发送Action给Store来执行具体的逻辑,达到UI和状态解耦的效果。
更多推荐




所有评论(0)