exportdefault(config: R.Config)=>({// ...
create(plugin: R.Plugin):R.Plugin{// ... do some validations
if(plugin.onInit){plugin.onInit.call(this);}constresult: R.Plugin|any={};if(plugin.exposed){for(constkeyofObject.keys(plugin.exposed)){this[key]=typeofplugin.exposed[key]==="function"?plugin.exposed[key].bind(this)// bind functions to plugin class
:Object.create(plugin.exposed[key]);// add exposed to plugin class
}}for(constmethodof["onModel","middleware","onStoreCreated"]){if(plugin[method]){result[method]=plugin[method].bind(this);}}returnresult;},});
exportdefaultclassRematch{// ...
publicaddModel(model: R.Model){// ...
// run plugin model subscriptions
this.forEachPlugin("onModel",(onModel)=>onModel(model));}publicinit() {// collect all models
this.models=this.getModels(this.config.models);for(constmodelofthis.models){this.addModel(model);}// ...
this.forEachPlugin("onStoreCreated",(onStoreCreated)=>{constreturned=onStoreCreated(rematchStore);// if onStoreCreated returns an object value
// merge its returned value onto the store
if(returned){Object.keys(returned||{}).forEach((key)=>{rematchStore[key]=returned[key];});}});returnrematchStore;}// ...
}
onModel 执行于遍历并添加 model 时,onStoreCreated 则当 store 创建完成并返回之前执行。前者通常用于读取、添加或修改 model 的配置,而后者则用于在 store 上添加新属性,如果有返回值且返回值是对象,则会将其上的属性都添加到 store 中。下面我们来看看两个具体的 plugin 以及这些钩子的应用。
constdispatchPlugin: R.Plugin={exposed:{// required as a placeholder for store.dispatch
storeDispatch(action: R.Action,state: any){console.warn("Warning: store not yet loaded");},storeGetState() {console.warn("Warning: store not yet loaded");},/**
* dispatch
*
* both a function (dispatch) and an object (dispatch[modelName][actionName])
* @param action R.Action
*/dispatch(action: R.Action){returnthis.storeDispatch(action);},/**
* createDispatcher
*
* genereates an action creator for a given model & reducer
* @param modelName string
* @param reducerName string
*/createDispatcher(modelName: string,reducerName: string){returnasync(payload?: any,meta?: any):Promise<any>=>{constaction: R.Action={type:`${modelName}/${reducerName}`};if(typeofpayload!=="undefined"){action.payload=payload;}if(typeofmeta!=="undefined"){action.meta=meta;}returnthis.dispatch(action);};},},// access store.dispatch after store is created
onStoreCreated(store: any){this.storeDispatch=store.dispatch;this.storeGetState=store.getState;return{dispatch: this.dispatch};},// generate action creators for all model.reducers
onModel(model: R.Model){this.dispatch[model.name]={};if(!model.reducers){return;}for(constreducerNameofObject.keys(model.reducers)){this.validate([[!!reducerName.match(/\/.+\//),`Invalid reducer name (${model.name}/${reducerName})`,],[typeofmodel.reducers[reducerName]!=="function",`Invalid reducer (${model.name}/${reducerName}). Must be a function`,],]);this.dispatch[model.name][reducerName]=this.createDispatcher.apply(this,[model.name,reducerName]);}},};
constdispatchPlugin: R.Plugin={exposed:{// required as a placeholder for store.dispatch
storeDispatch(action: R.Action,state: any){console.warn("Warning: store not yet loaded");},storeGetState() {console.warn("Warning: store not yet loaded");},/**
* dispatch
*
* both a function (dispatch) and an object (dispatch[modelName][actionName])
* @param action R.Action
*/dispatch(action: R.Action){returnthis.storeDispatch(action);},},// access store.dispatch after store is created
onStoreCreated(store: any){this.storeDispatch=store.dispatch;this.storeGetState=store.getState;return{dispatch: this.dispatch};},};
由于 rematch store 是对 redux store 的增强,依赖于 redux store。因此,在 redux store 创建完成以前,是不可以访问 storeDispatch,storeGetState 和 dispatch 的。而创建完以后,首先是需要覆盖掉 storeDispatch 和 storeGetState,然后返回一个增强的 dispatch,覆盖掉 redux store 原先的 dispatch。
consteffectsPlugin: R.Plugin={exposed:{// expose effects for access from dispatch plugin
effects:{},},// add effects to dispatch so that dispatch[modelName][effectName] calls an effect
onModel(model: R.Model):void{if(!model.effects){return;}consteffects=typeofmodel.effects==="function"?model.effects(this.dispatch):model.effects;for(consteffectNameofObject.keys(effects)){// ... some validations
this.effects[`${model.name}/${effectName}`]=effects[effectName].bind(this.dispatch[model.name]);// add effect to dispatch
// is assuming dispatch is available already... that the dispatch plugin is in there
this.dispatch[model.name][effectName]=this.createDispatcher.apply(this,[model.name,effectName]);// tag effects so they can be differentiated from normal actions
this.dispatch[model.name][effectName].isEffect=true;}},// process async/await actions
middleware(store){return(next)=>async(action: R.Action)=>{// async/await acts as promise middleware
if(action.typeinthis.effects){awaitnext(action);returnthis.effects[action.type](action.payload,store.getState(),action.meta);}returnnext(action);};},};