# StateStore **Repository Path**: daryl_code/StateStore ## Basic Information - **Project Name**: StateStore - **Description**: HarmonyOS全局状态管理解决方案 - **Primary Language**: TypeScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 12 - **Created**: 2024-11-29 - **Last Updated**: 2024-11-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # HMState简介 StateStore作为HarmonyOS状态与UI解耦的解决方案,支持全局维护状态,优雅地解决状态共享的问题。 StateStore库提供共享模块StateStore单例,支持根据唯一标识创建store存储对象,管理应用的全局状态,通过分发action更新状态。依赖系统@Observed和@ObservedV2对数据改变监听的能力,驱动UI刷新。 目的是让开发者在开发过程中实现状态与UI解耦,多个组件可以方便地共享和更新全局状态,将状态管理逻辑从组件逻辑中分离出来,简化维护。 # 特性 + 状态与UI解耦,支持数据全局化操作 + 简化子线程并行化操作 + 支持通过中间件对Reducer逻辑执行预处理和后处理 # 依赖系统版本 - HarmonyOS NEXT Release 5.0及以上 > 手机版本:5.0.0.106以上 # 项目版本信息 当前最新版本 | 名称 | 说明 | 版本号 | 发布状态 | |--------------------------------------|----------|------------|------| | [@hadss/state_store](https://xxxxxx) | 全局状态管理框架 | 1.0.0-rc.0 | 未发布 | # 下载安装 ## 使用ohpm安装依赖 ```shell ohpm install @hadss/state_store ``` > 或者按需在模块中配置运行时依赖,修改oh-package.json5 ```json5 { "dependencies": { "@hadss/state_store": "^1.0.0-rc.0" } } ``` # 快速开始 > 以下关于装饰器的使用,采用状态管理V2版本举例 ## 定义被观察的数据 开发者根据使用的harmonyOS的状态管理版本,V1版本使用`@Observed`观察数据,V2版本使用`@ObservedV2+@Trace`观察数据。 数据改变自动刷新UI的能力依赖harmonyOS装饰器的观察能力。 ```extendtypescript @ObservedV2 export class Info { @Trace id: number = 0 @Trace name: string = '初始状态' constructor(id: number, name: string) { this.id = id this.name = name } } @ObservedV2 export class ListData { @Trace data: Info[] = [] @Trace loading: boolean = false @Trace error: string | null = null @Trace title: string = '2024.11.6' } export let model: ListData = new ListData() ``` ## 定义Reducer纯函数 数据集中处理的纯函数,支持同步和异步逻辑处理。Reducer同步执行返回`null`, 异步执行返回Promise函数。 ```extendtypescript import { Reducer } from '@hadss/state_store' const rootReducer: Reducer = (_state: ListData, action: Action) => { switch (action.type) { case 'changeTitle': _state.title = '给StateStore点赞' break case 'getList': return async () => { _state.loading = true _state.error = null try { // 模拟异步操作 const data = await new Promise((resolve) => { setTimeout(() => { resolve(mockData); }, 1000); // 模拟 1 秒的延迟 }); _state.loading = false _state.data = data _state.title = '这是一个StateStore库' } catch (error) { _state.loading = true _state.data = [] _state.error = error.message } }; default: break; } return null } ``` ## 定义Action对象 通过`StateStore`提供的`createAction`方法创建action对应事件的action对象,用于dispatch分发事件传参和reducer纯函数中与type类型匹配执行对应逻辑。 ```extendtypescript const GetListAction = StateStore.createAction("getList") const ChangeTitleAction = StateStore.createAction("changeTitle") ``` ## 创建store对象 导入StateStore单例模块,把数据实例和Reducer统一加到`createStore`方法中,生成store对象。。 ```extendtypescript import { StateStore, Store } from '@hadss/state_store' export let store: Store = StateStore.createStore('store1', model, rootReducer) ``` ## 在UI组件中使用数据和触发事件 UI组件中通过`store.getState()`获取数据和系统对数据的监听绑定关系。`store.dispatch`传入定义的Action对象,触发reducer中对应的action事件 ```extendtypescript @Entry @ComponentV2 struct AsyncList { @Local data: ListData = store.getState() aboutToAppear(): void { store.dispatch(GetList) } build() { Column() { Row(){ Text(this.data.title).fontSize(20) } List() { ForEach(this.data.data, (item: Info, index: number) => { ListItem() { Row(){ Text(`${item.id}---${item.name}`).fontSize(20) } .width('80%') .backgroundColor(Color.Pink) } }, (item: Info) => item.id.toString()) } .height('50%') .width('100%') .backgroundColor(Color.Gray) Button('修改名字').onClick(() => { store.dispatch(ChangeTitle) }) } } } ``` ## 子线程并行化操作并刷新UI + `StateStore`是sendable对象,可以直接在子线程使用`StateStore.createSendableAction`方法创建一个在子线程中使用的Action; + 将子线程的Action对象传递到主线程接收; + 主线程在接收的回调内使用`StateStore.receiveSendableAction`触发Reducer里定义的对应type类型的函数逻辑; `StateStore库`提供`createSendableAction`方法创建在子线程中使用的Action对象, 和主线程触发Reducer逻辑的`receiveSendableAction`方法。 ### UI组件定义 定义UI组件触发子线程函数执行 ```extendtypescript @Entry @Component struct TaskpoolPage { @State student: Student = store.getState(); build() { Column(){ Text(`ObservedClass.describe: ${this.student.describe}`).fontSize(20); Button("改变内容").onClick(()=>{ execTaskFunc() }) } } } ``` ### 子线程定义 + 在Reducer纯函数定义子线程对应type类型的数据处理逻辑; + 子线程通过`StateStore.createSendableAction`生成一个可以传递给主线程的Sendable化的Action对象; + 主线程接收action对象,`StateStore.receiveSendableAction`触发与Reducer中type匹配的逻辑; ```extendtypescript import { StateStore, SendableAction } from '@hadss/state_store' const rootReducer: Reducer = (_state: Student,action: Action) =>{ switch (action.type){ case 'UPDATE': // ... break; default: break; } return null; } @Concurrent function sendDataTest(data: CustomData): void { try { const title = '这是一条来自子线程的数据' let action: SendableAction = StateStore.createSendableAction('store1', 'UPDATE', title) taskpool.Task.sendData(action) } catch (err) { } } function printResult(data: SendableAction): void { StateStore.receiveSendableAction(data) } export async function execTaskFunc(){ try { let task: taskpool.Task = new taskpool.Task(sendDataTest); task.onReceiveData(printResult) await taskpool.execute(task) } catch (err) { } } ``` ## 中间件进行Reducer逻辑的预处理和后处理 我们引入了中间件(middleware)的概念,进行加强dispatch的功能,以满足开发者进行复杂的业务逻辑处理。 `StateStore.createStore`方法第四个参数支持传递middleware的数组集合。通过中间件在dispatch过程中插入自定义的逻辑,可以监听和扩展action的行为。 每个中间件都可以定义beforeAction和afterAction函数。执行顺序由注册的先后顺序决定。在StateStore的dispatch的执行过程流程:action ->middlewares.beforeAction -> reducer-> middlewares.afterAction **执行顺序规则:** 1. beforeAction 执行顺序: + 按照注册顺序依次执行。 + 第一个注册的中间件的 before 最先执行,接着是第二个,依此类推。 2. afterAction 执行顺序: + 按照注册顺序的逆序执行。 + 最后一个注册的中间件的 after 最先执行,倒数第二个的 after 接着执行,依此类推。 **中间件的常见使用场景包括:** + 日志记录:记录每个 action 的触发和对应的状态变化。 + 权限控制:在触发 action 前检查权限或条件 + 数据预处理/后处理:在 action 执行前后修改或检查数据。 + 调试工具:帮助开发者调试状态管理逻辑。 + 拦截/修改 action:在 action 执行前后拦截或修改触发的 action 数据。 + 异步任务管理:执行额外的异步任务,比如网络请求或调度任务。 **定义中间件示例** ### 定义中间件 通过StateStore库提供的抽象类`Middleware`,开发者继承该抽象类自定义一个中间件的类,根据业务逻辑生成需要的中间件。 ```extendtypescript import { Middleware } from '@hadss/state_store'; // 继承的中间件类的形式开发者自定义 class MiddlewareInstance extends Middleware { beforeAction: MiddlewareFuncType afterAction: MiddlewareFuncType constructor(beforeAction: MiddlewareFuncType, afterAction: MiddlewareFuncType) { super() this.beforeAction = beforeAction this.afterAction = afterAction } } // 日志监控的中间件 let logMiddleware = new MiddlewareInstance((state: ListData, action: Action) => { console.log('logMiddleware-before:', state.title); return MiddlewareStatus.NEXT }, (state: ListData, action: Action) => { console.log('logMiddleware-after:', state.title); return MiddlewareStatus.NEXT }) ``` ### store绑定中间件 ```extendtypescript //将中间件logMiddleware, authMiddleware 注册到 对应的store中 export let store: Store = StateStore.createStore('store1', model, rootReducer, [logMiddleware]) ``` ## 合并Reducer函数 在相对比较复杂的业务中,单一reducer可能难以维护和理解。此处我们设计提供了combineReducers方法,开发者可以将复杂的state切分多个更小、更具体的片段,每个片段有专门的子reducer进行管理。然后通过combineReducers将多个子reducer进行组合成一个根reducer; **combineReducers示例** 1. 定义子reducers ```extendtypescript import { Reducer } from '@hadss/state_store'; @ObservedV2 export class CounterState { @Trace count: number = 0 }; export const counterReducer: Reducer = (state: CounterState, action: Action) => { switch (action.type) { case INCREMENT.type: state.count ++ break case DECREMENT.type: state.count-- // return state break default: break } return null }; // todosReducer.ts @ObservedV2 export class Todo { @Trace id: number = 0; @Trace text: string = ''; @Trace completed: boolean = false; // ... } @ObservedV2 export class TodosState { @Trace todos: Todo[] = [] }; export const todosReducer: Reducer = (state: TodosState, action: Action) => { switch (action.type) { case ADD_TODO.type: state.todos = [...state.todos, action.payload] break case TOGGLE_TODO.type: state.todos.forEach(todo => { if (todo.id === action.payload) { todo.completed = !todo.completed } }) break default: break } return null } ``` 2. 使用combineReducers进行初始化store 将监听的数据合并到一个class类中,对应处理的reducer纯函数合并到一个Reducer。同时注意根Reducer的key值要和监听的属性名保持一致。 ```extendtypescript import { StateStore } from '@hadss/state_store' @ObservedV2 class RootState { @Trace counter: CounterState = new CounterState(); @Trace todos: TodosState = new TodosState(); } let model = new RootState() const rootReducer = StateStore.combineReducers({ "counter": counterReducer, "todos": todosReducer, }); export let store: Store = StateStore.createStore('RootState', model, rootReducer) ``` # StateStore接口和属性列表 [查看详情](docs/Reference.md) # SampleCode 本项目包含 Sample示例代码(此处Sample链接) ,通过TODO待办列表展示StateStore在全局状态管理场景中的使用 # FAQ [查看详情](docs/FAQ.md) # 原理介绍 本解决方案的思路参考[redux](https://redux.js.org/api/store)和[vuex](https://vuex.vuejs.org/guide/actions.html)的全局状态管理的实现。具体原理可以学习redux和vuex,对理解本库的实现和使用有帮助。 # 贡献代码 使用过程中发现任何问题都可以提 [Issue]() ,当然,也非常欢迎发 [PullRequest]() 共建。 # 开源协议 本项目基于 [Apache License 2.0]() ,请自由地享受和参与开源。