状态管理
icejs 内置了状态管理方案,并在此基础上进一步遵循 “约定优于配置” 原则,进行抽象和封装,使得状态管理变得非常容易。
#
全局应用状态#
定义 Model约定全局状态位于 src/models
目录,目录结构如下:
假设我们需要全局管理用户状态,定义模型如下:
#
初始化 Store详细文档请参考 API-createStore 。
#
在 View 中使用模型状态#
页面级状态页面状态只能在该页面下的组件中使用,无法跨页面使用。
#
非嵌套页面大部分场景中一个 pages/Foo
目录对应一个路由,即非嵌套页面,此时约定页面状态在 src/pages/*/models
中定义。
目录组织如下:
定义模型如下:
初始化 Store 实例:
在页面组件中使用模型状态:
#
嵌套页面某些复杂场景会出现嵌套页面的情况,即 src/pages/Home
下包含多个路由页面,目录组织如下:
对于嵌套页面,框架会将 store 的 Provider 包裹在 Layout/index.tsx
上,此时所有嵌套的页面以及组件都可以访问到这里的 store。Layout/index.tsx
内容如下:
同时配置在 src/routes.ts
中:
#
高阶用法#
设置初始状态如果定义了
app.getInitialData()
,该方法返回的initialStates
字段会默认赋值给store.initialStates
页面级状态目前不支持设置 initialStates
#
TypeScript 类型提示编写类型有助于更好的代码提示,类型定义步骤如下:
- 创建 Store 实例
- 定义状态模型
#
Model 定义详细说明如上示例所述,icejs 约定在 src/models
、src/pages/*/models
目录下的文件为项目定义的模型文件,每个文件需要默认导出一个对象。
通常模型定义包括 state、reducers、effects 三部分:
state
model 的初始 state
reducers
一个改变该模型状态的函数集合。这些方法以模型的上一次 prevState 和一个 payload 作为入参,在方法中使用可变的方式来更新状态。这些方法应该是仅依赖于 prevState 和 payload 参数来计算下一个 nextState 的纯函数。对于有副作用的函数,请使用 effects。
effects
一个可以处理该模型副作用的函数集合。这些方法以 payload 和 rootState 作为入参,适用于进行异步调用、模型联动等场景。
#
Model 之间通信注意:如果两个 model 不属于同一个 store 实例,是无法通信的
在 effects 里的 action 方法中可以通过 dispatch[model][action]
拿到其他模型所定义的方法。
如果遇到
this.foo
的 ts 类型错误,请参考文档 icestore QA 进行修复
setState 是 icestore 内置的一个 reducer,可以直接使用
#
Model 中使用 immer 更改 stateRedux 默认的函数式写法在处理一些复杂对象的 state 时会非常繁琐,因此 icejs 同时支持了使用 immer 来操作 state:
注意:因为 immer 无法支持字符串或数字这样的简单类型,因此如果 state 符合这种情况(极少数)则不支持通过 immer 操作,必须使用 Redux 默认的函数式写法(返回一个新值):
#
获取 effects 的状态 loading/error通过 useModelEffectsState
API 即可获取到 effects 的 loading 和 error 状态。
#
在 Class Component 中使用useModel 相关的 API 基于 React 的 Hooks 能力,仅能在 Function Component 中使用,通过 withModel
API 可以实现在 Class Component 中使用。
同时,也可以使用 withModelDispatchers
以及 withModelEffectsState
API。
#
Redux Devtoolsicejs 中默认集成了 Redux Devtools,不需要额外的配置就可以通过 Redux Devtools 调试:
如果想要定义 Devtools 的参数,可以查看上面 createStore
的 options 说明。
#
在其他地方使用 store满足以下几种情况,框架都会自动帮助开发者包裹 store.Provider
:
- SPA 全局 store:
src/
下有store.ts
和app.ts
- SPA 页面级 store:
src/pages/Home
下有store.ts
和index.tsx
- SPA 嵌套页面级 store:
src/pages/Home
下有store.ts
和Layout/index.tsx
(优先级低于上面) - MPA 组件类型的 entry:
src/pages/Home
下有store.ts
和index.tsx
- MPA 单页类型的 entry:
src/pages/Home
下有store.ts
,app.ts
,Layout/index.tsx
如果不满足上述情况,则需要开发者自行包裹 store.Provider
。比如希望在 src/pages/Home/Foo/
下创建一个 store:
- 在
src/pages/Home/Foo/models/
下定义 model - 在
src/pages/Home/Foo/store.ts
中初始化 store - 新增步骤: 在
src/pages/Home/Foo/index.tsx
中包裹store.Provider
#
使用其他状态管理方案icejs 默认使用 @ice/store 作为状态管理方案,如需使用其他方案,需要在 build.json 中通过选项关闭默认方案:
此时项目不会再引入 @ice/store
相关的各种能力,包含上述的自动包裹 Provider
等,此时就可以灵活的引入其他状态管理方案了。
#
版本变更说明#
内置的 immer 从 6.x 升级到最新版本 9.xicejs 2.0.0 版本升级
#
不再自动初始化 store1.9.7 版本标记废弃,2.0.0 版本完全移除
推荐开发者自行创建 store.ts
并在其中初始化 store,这样可以更灵活的定制一些参数,相对之前方案带来的改变:
- 开发者需要在
models/
同层目录自行创建store.ts
并初始化 store 实例:
- 引入 store 的路径发生了变化:
actions: {}
写法#
Model 中不再支持 1.7.0 版本标记废弃,2.0.0 版本完全移除
将原先的 actions: {}
拆分为 effects: () => {}
和 reducers: {}
两个字段:
store.getInitialStates()
#
不再支持 1.7.0 版本标记废弃,2.0.0 版本完全移除
推荐使用 store.initialStates
。
#
路由切换后重新初始化页面状态icejs 1.0 中有一个「错误」的设计,切换页面再次进入原页面后页面状态不会重新初始化,如需重新初始化需要主动配置:
icejs 2.0 版本将此默认行为进行了修正,切换页面再次进入原页面后会重新初始化页面状态,如果希望跟 1.0 表现一致,则需要主动配置: