Action
Sigi 的 EffectModule
底层由一个名为 State
的对象实现。State
对象和 Redux
的 Store
非常类似。它总是接收从 Dispatcher
和 Effect
发送过来的 Action
, 并且将它们传递给 Reducer
和 Effect
。
在使用 Sigi 的应用中,你不需要手动创建 Action
,所以我们并没有对外提供 Dispatch
方法。取而代之有两种方法可以达到 Dispatch
action 的效果:
- 调用 Dispatcher 上的方法
- 在 Effect 中发射 (emit) action
Dispatcher & ActionsCreator
Dispatcher 是一个包含丰富类型定义的 ActionsCreator 的抽象。
所有 EffectModule
类上被 Reducer, ImmerReducer, Effect 和 DefineAction 装饰器装饰的方法,都在 EffectModule
类中的 ActionsCreator 对象上有一个对应的方法,也在 Component
中的 Dispatcher 上有一个对应的方法。EffectModule
类中的 ActionsCreator 用来生成一个 Action
并且发射(emit) 给 State
对象,Component
中的 Dispatcher 用来直接 Dispatch
一个 Action 给 State
对象。
Dispatch props created by Redux-Actions
你可能在写 Redux
的时候用过 Redux-Actions 这个库。Sigi
中的 ActionsCreator 概念与 Redux-Actions
库中的 ActionsCreator 非常类似。在 Redux
应用中 ActionsCreator 有几个明显的好处:
- 避免在程序中编写 魔法字符串
- (在 TypeScript 应用中)提供类型信息
- 减少样板代码
下面的例子展示了在 Redux
应用中使用和不使用 Redux-Actions
的区别:
// raw dispatcherconnect(mapStateToProps, (dispatch) => bindActionCreators({addCount: (count: number) => dispatch({ // losing type information heretype: 'ADD_COUNT',payload: count,}),}, dispatch))// reducerexport const reducer = (state, action) => { // losing type information hereswitch action.type:case: 'ADD_COUNT':return { ...state, count: state.count + payload }default:return { count: 0 }}
const ADD_COUNT = createAction<number>("ADD_COUNT");interface DispatchProps {addOne: typeof ADD_COUNT;}interface StateProps {count: number;}// react actions dispatcherconnect(mapStateToProps, (dispatch) =>bindActionCreators({addCount: ADD_COUNT,} as DispatchProps,dispatch));// reducerexport const reducer = handleActions({[`${ADD_COUNT}`]: (state: StateProps, { payload }: Action<number>) => {return { ...state, count: state.count + payload };},},{ count: 0 });
使用 Sigi 管理 actions
定义 EffectModule
import { Module, EffectModule, Reducer } from "@sigi/core";interface State {count: number;}@Module("SomeNamespace")class SomeModule extends EffectModule<State> {defaultState = {count: 0,};@Reducer()add(state: State, count: number) {return { ...state, count: state.count + count };}}
可以从上面的代码看到,没有任何魔法字符串,也没有冗余的样板代码 🤯。
使用
对使用 React 开发的应用,我们提供了两个 hooks 来在组件中引用 effect module 里定义的操作。 这两个 hooks 都接收 EffectModule 类型作为参数,并返回 Module 对于的 dispatcher
useModule
和 useDispatchers
// 使用 moduleimport { useDispatchers, useModule } from "@sigi/react";const SomeComponent = () => {// 注意:我们不需要担心 dispatcher 的类型。// Sigi 的类型系统可以保证所有定义在 module 中的 reducers/effects 的类型都能 100% 正确地被推导出来。const [state, dispatcher] = useModule(SomeModule);// 如果某个组件不关心 module 的状态变更(即便状态更新了,也不需要重新渲染),可以这么使用:// const dispatcher = useDispatchers(SomeModule);const onClickAdd = useCallback(() => {// ts 会对 payload 进行类型检查// 也可以直接在这里使用**跳转到定义**类似的编辑器功能!dispatcher.add(1);// 下面这行会出现 ts 类型错误// dispatch.add('string value');}, [dispatcher]);return (<div><button onClick={onClickAdd}>add 1</button><span>count: {state.count}</span></div>);};