yax
Version:
Yet another store using redux. (Inspired by vuex and dva)
140 lines (122 loc) • 3.8 kB
JavaScript
import { createStore, applyMiddleware, compose } from 'redux';
import ModuleCollection from './module/module-collection';
import { isObject, isPromise, getNestedState, assert } from './util';
export default function yax (options = {}, enhancer) {
let _actions = {};
const _modules = new ModuleCollection(options);
// init root module.
// this also recursively registers all sub-modules
_installModule([], _modules.root);
const middleware = () => (next) => (action) => {
if (isObject(action)) {
const { type, payload } = action;
const entry = _actions[type];
if (entry) {
return entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload);
}
}
return next(action);
};
const rootMW = applyMiddleware(middleware);
const _store = createStore(
_modules.makeReducers(),
options.state || {},
enhancer ? compose(enhancer, rootMW) : rootMW
);
function registerModule (path, rawModule) {
if (typeof path === 'string') path = [path];
assert(Array.isArray(path), `module path must be a string or an Array.`);
_modules.register(path, rawModule);
_installModule(path, _modules.get(path));
_resetReducers();
}
function unregisterModule (path) {
if (typeof path === 'string') path = [path];
assert(Array.isArray(path), `module path must be a string or an Array.`);
_modules.unregister(path);
_resetStore();
_store.dispatch({ type: '@@yax/unregister', path });
}
return {
registerModule,
unregisterModule,
..._store
};
/**
* Private Methods
*/
// make localized dispatch, commit and state
function _makeLocalContext (namespace, path) {
const wrapFn = (type, payload, isRoot) => {
let action = { type, payload };
if (isObject(type) && type.type) {
isRoot = payload;
action = type;
}
action.type = isRoot ? action.type : namespace + action.type;
// redux dispatch
return _store.dispatch(action);
};
const local = {
dispatch: wrapFn,
commit: wrapFn,
select: (fn) => {
// redux state
const rootState = _store.getState();
const state = getNestedState(rootState, path);
return fn ? fn(state, rootState) : state;
}
};
return local;
}
function _registerAction (type, handler, local) {
const entry = _actions[type] || (_actions[type] = []);
entry.push(function wrappedActionHandler (payload) {
let res = handler({
dispatch: local.dispatch,
commit: local.commit,
select: local.select
}, payload);
if (!isPromise(res)) {
res = Promise.resolve(res);
}
return res;
});
}
function _installModule (path, module) {
const namespace = _modules.getNamespace(path);
const local = module.context = _makeLocalContext(namespace, path);
module.cleanReducers();
module.forEachReducer((reducer, key) => {
const actionType = namespace + key;
module.addReducer(actionType, reducer);
});
module.forEachAction((action, key) => {
const actionType = namespace + key;
_registerAction(actionType, action, local);
});
module.forEachChild((child, key) => {
_installModule(path.concat(key), child);
});
}
function _resetReducers () {
const reducer = _modules.makeReducers();
_store.replaceReducer(reducer);
}
function _resetStore () {
_actions = {};
// init all modules
_installModule([], _modules.root);
_resetReducers();
}
}
export {
combineReducers,
bindActionCreators,
applyMiddleware,
compose
} from 'redux';
export { composeReducers, mapReducers } from './util';
export { mapState, mapActions } from './helpers';