UNPKG

cumqueoptio

Version:

The meta-framework suite designed from scratch for frontend-focused modern web development.

129 lines (110 loc) 3.21 kB
import { createPlugin } from '@modern-js-reduck/store'; import { Model } from '@modern-js-reduck/store/types'; import { createPromise } from 'redux-promise-middleware'; type AsyncEffect = (...args: any[]) => Promise<any>; type VoidEffect = (...args: any[]) => void; type ThunkEffect = (...args: any[]) => () => any; interface Effects { [key: string]: AsyncEffect | VoidEffect | ThunkEffect | Effects; } declare module '@modern-js-reduck/store' { // Add `effects` type when use model({effects}). interface ModelDesc { effects?: Effects; } // Overload GetActions interface to add actions type to useModel's return interface GetActions<M extends Model> { effectsActions: M['_']['effects'] & { [key in keyof M['_']['actions']]: unknown; }; } } const isReduxPromiseFulfilled = (data: any) => { return ( typeof data === 'object' && data.hasOwnProperty('action') && data.hasOwnProperty('value') ); }; const isPromise = (value: any) => { if (value !== null && typeof value === 'object') { return value && typeof value.then === 'function'; } return false; }; /** * Generate dispatch action from effects definitions. */ const createDispatchActionsFromEffects = ( store: any, name: string, effects: Effects, setDispatchAction: (path: string[], action: any) => void, ) => { const path = [name]; const traverse = (_effects: Effects[string]) => { if (typeof _effects === 'function') { const type = path.join('/').toUpperCase(); setDispatchAction(path.slice(), (...args: any[]) => { const value = (_effects as (..._args: any[]) => any)(...args); const dispatch = (payload: any) => store.dispatch({ type, payload, }); // Handled by promise middleware or redux thunk // Otherwise, do not dispatch action, just exec the effect function. if (isPromise(value) || typeof value === 'function') { const res = dispatch(value); if (isPromise(res)) { // parse redux-promise result, return orginal value of the effect return res.then((data: any) => isReduxPromiseFulfilled(data) ? data.value : data, ); } return res; } return value; }); } else { Object.keys(_effects).forEach(key => { path.push(key); traverse(_effects[key]); path.pop(); }); } }; traverse(effects); }; const plugin = createPlugin(context => ({ config(storeConfig) { return { ...storeConfig, middlewares: [ createPromise({ promiseTypeDelimiter: '/' }), // middlewares from config are at the end ...(storeConfig.middlewares || []), ], }; }, modelMount({ modelDesc, mountedModel }, { setDispatchAction }) { const { effects } = modelDesc; if (!effects) { return { modelDesc, mountedModel, }; } createDispatchActionsFromEffects( context.store, modelDesc.name, modelDesc.effects!, setDispatchAction, ); return { modelDesc, mountedModel, } as any; }, })); export default plugin;