Action
Sigi EffectModule was implemented by State object under the hood. State object is very similar with redux Store object. It Dispatch actions to reducers and effects, and receive actions from Dispatcher and Effect.
In sigi application, you will never need to create an Action by hand, so we do not expose the Dispatch function.
In replacement, there are two ways to dispatch action:
- call methods on Dispatcher
- emit actions in Effect
Dispatcher & ActionsCreator
Dispatcher is a high level abstraction for ActionsCreator with well defined type information.
Every method in EffectModule class decorated by Reducer, ImmerReducer, Effect , DefineAction Decorators match one property with the same name in ActionsCreator in EffectModule class, and match one property with the same name in Dispatcher in Component. The ActionsCreator in EffectModule class is used for emit action in Effect, and the Dispatcher in Component is used for Dispatch actions to State.
Dispatch props created by Redux-Actions
You may have used Redux-Actions in Redux application. ActionsCreator in sigi is very similar with ActionsCreator in Redux-Actions.And there are few advantages for create ActionsCreator using Redux-Actions:
- Avoid Magic string
- Provide type information
- Reduce boilerplate codes
// 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 });
The Sigi way
Definition
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 };}}
As you can see above, in the definition part of a sigi app, there is no magic action string, no redundant boilerplate 🤯.
Usage
For react APP, we provide two hooks that let you refer actions defined in effect modules. both of them receive a EffectModule class and produce a dispatcher referred to this module.
useModule and useDispatchers
// use moduleimport { useDispatchers, useModule } from "@sigi/react";const SomeComponent = () => {// note: we don't need to worry about the types of dispatcher.// Sigi's type system will ensure all defined actions/effects are 100% accurate referred in dispatcherconst [state, dispatcher] = useModule(SomeModule);// or, if you don't care about the state changes.// const dispatcher = useDispatchers(SomeModule);const onClickAdd = useCallback(() => {// ts will perform typecheck in the payload part// and you can also perform **Jump to definition** in code editordispatcher.add(1);// ts type error// dispatch.add('string value')}, [dispatcher]);return (<div><button onClick={onClickAdd}>add 1</button><span>count: {state.count}</span></div>);};