mobx-roof
Version:
Simple React data management by mobx.
128 lines (126 loc) • 3.97 kB
JavaScript
import { each, mapValues } from '../common/utils';
import MobxModel, { isMobxModelClass } from './MobxModel';
import globalMiddleware from './globalMiddleware';
import MobxRelation from './MobxRelation';
import SimpleEvent from '../common/SimpleEvent';
export default class MobxContext extends SimpleEvent {
/**
* @param {Object} contextInitData
* @param {Object} opts
* - parentContext
* - middleware
* - relation
* - transfer
*/
constructor(contextInitData = {}, opts = {}) {
super();
this._middleware = opts.middleware || globalMiddleware;
this._relation = opts.relation || new MobxRelation;
this._data = mapValues(contextInitData, (Model, name) => {
// Get from parent context
if (typeof Model === 'string') {
if (opts.parentContext) {
return opts.parentContext.find(Model);
}
throw new Error(`[@context] InitData "${Model}" can not find in it's parentContext.`);
}
// Get a class
if (isMobxModelClass(Model)) {
return new Model(null, this._middleware);
}
// Get an instance
if (Model instanceof MobxModel) {
// update model's middleware
Model.middleware = this._middleware;
return Model;
}
throw new TypeError(`[@context] ${name} must instance of MobxModel or MobxModel class.`);
});
// transfer middleware and relation
if (opts.parentContext) {
this.transferFromParentContext(opts.parentContext, opts);
}
this._addRelationMiddleware();
// trigger relation init function in async
setTimeout(() => {
this._relation.triggerInit(this);
this._relation.triggerAutorun(this);
});
}
transferFromParentContext(parentContext, opts) {
this._middleware = parentContext.middleware;
// transfer customer
if (opts.transfer) {
opts.transfer(this, opts.parentContext);
}
}
_addRelationMiddleware() {
if (this._relationRemove) return;
const hook = (arg) => {
const fullname = `${this._findKeyByModel(arg.model)}.${arg.action}`;
// exec async
setTimeout(() => {
this._relation.execInMiddleware({ ...arg, fullname, context: this });
});
return arg.payload;
};
this._relationRemove = this.middleware.use({
after: hook,
});
}
set relation(newRelation) {
this._relation = newRelation;
}
get relation() {
return this._relation;
}
get middleware() {
return this._middleware;
}
get data() {
return this._data;
}
destroy() {
// remove relation middleware
if (this._relationRemove) {
this._relationRemove();
this._relationRemove = null;
}
}
checkMobxModels(mobxModels) {
if (Array.isArray(mobxModels)) {
mobxModels.forEach((name) => {
if (!this._data[name]) {
throw new Error(`[@observer] Can not find data "${name}" in MobxContext.`);
}
});
} else {
each(mobxModels, (MobxModel, name) => {
if (this._data[name]) {
if (!isMobxModelClass(MobxModel)) {
throw new TypeError(`[@observer] MobxContext required MobxModel class.`);
}
if (!(this._data[name] instanceof MobxModel)) {
throw new TypeError(`[@observer] ${name} is not instance of ${MobxModel.name}.`);
}
} else {
throw new Error(`[@observer] Can not find data "${name}" in MobxContext.`);
}
});
}
}
pick(...keys) {
return keys.reduce((obj, key) => {
if (!this._data[key]) throw new Error(`[MobxContext] Can not find data "${key}" in MobxContext.`);
obj[key] = this._data[key];
return obj;
}, { });
}
find(key) {
if (!this._data[key]) throw new Error(`[MobxContext] Can not find data "${key}" in MobxContext.`);
return this._data[key];
}
_findKeyByModel(model) {
return Object.keys(this._data).find(key => this._data[key] === model);
}
}