ayanami
Version:
A better way to react with state
187 lines (186 loc) • 7.82 kB
JavaScript
import { __assign } from "tslib";
import { merge, Subject, Subscription, NEVER } from 'rxjs';
import { map, catchError, takeUntil, filter } from 'rxjs/operators';
import mapValues from 'lodash/mapValues';
import produce from 'immer';
import { createState, getEffectActionFactories, getOriginalFunctions } from './utils';
import { logStateAction } from '../redux-devtools-extension';
import { ikariSymbol } from './symbols';
import { TERMINATE_ACTION } from '../ssr/terminate';
import { isSSREnabled } from '../ssr/flag';
function catchRxError() {
return catchError(function (err) {
console.error(err);
return NEVER;
});
}
export function combineWithIkari(ayanami) {
var ikari = Ikari.getFrom(ayanami);
if (ikari) {
return ikari;
}
else {
var _a = getOriginalFunctions(ayanami), effects = _a.effects, reducers = _a.reducers, immerReducers = _a.immerReducers, defineActions = _a.defineActions;
Object.assign(ayanami, mapValues(defineActions, function (_a) {
var observable = _a.observable;
return observable;
}));
return Ikari.createAndBindAt(ayanami, {
nameForLog: ayanami.constructor.name,
defaultState: ayanami.defaultState,
effects: effects,
reducers: reducers,
immerReducers: immerReducers,
defineActions: defineActions,
effectActionFactories: getEffectActionFactories(ayanami),
});
}
}
export function destroyIkariFrom(ayanami) {
var ikari = Ikari.getFrom(ayanami);
if (ikari) {
ikari.destroy();
Reflect.deleteMetadata(ikariSymbol, ayanami);
}
}
var Ikari = /** @class */ (function () {
// eslint-disable-next-line @typescript-eslint/explicit-member-accessibility
function Ikari(ayanami, config) {
var _this = this;
this.ayanami = ayanami;
this.config = config;
this.state = createState(this.config.defaultState);
this.effectActionFactories = this.config.effectActionFactories;
this.triggerActions = {};
this.subscription = new Subscription();
// @internal
this.terminate$ = new Subject();
this.log = function (_a) {
var originalActionName = _a.originalActionName, effectAction = _a.effectAction, reducerAction = _a.reducerAction;
if (effectAction && effectAction !== TERMINATE_ACTION) {
logStateAction(_this.config.nameForLog, {
params: effectAction.params,
actionName: originalActionName + "/\uD83D\uDC49" + effectAction.ayanami.constructor.name + "/\uFE0F" + effectAction.actionName,
});
}
if (reducerAction) {
logStateAction(_this.config.nameForLog, {
params: reducerAction.params,
actionName: originalActionName,
state: reducerAction.nextState,
});
}
};
this.handleAction = function (_a) {
var effectAction = _a.effectAction, reducerAction = _a.reducerAction;
if (effectAction) {
if (effectAction !== TERMINATE_ACTION) {
var ayanami = effectAction.ayanami, actionName = effectAction.actionName, params = effectAction.params;
combineWithIkari(ayanami).triggerActions[actionName](params);
}
else {
_this.terminate$.next(effectAction);
}
}
if (reducerAction) {
_this.state.setState(reducerAction.nextState);
}
};
var _a = setupEffectActions(this.config.effects, this.state.state$), effectActions$ = _a[0], effectActions = _a[1];
var _b = setupReducerActions(this.config.reducers, this.state.getState), reducerActions$ = _b[0], reducerActions = _b[1];
var _c = setupImmerReducerActions(this.config.immerReducers, this.state.getState), immerReducerActions$ = _c[0], immerReducerActions = _c[1];
this.triggerActions = __assign(__assign(__assign(__assign({}, effectActions), reducerActions), immerReducerActions), mapValues(this.config.defineActions, function (_a) {
var next = _a.next;
return next;
}));
var effectActionsWithTerminate$;
if (!isSSREnabled()) {
effectActionsWithTerminate$ = effectActions$;
}
else {
effectActionsWithTerminate$ = effectActions$.pipe(takeUntil(this.terminate$.pipe(filter(function (action) { return action === null; }))));
}
this.subscription.add(effectActionsWithTerminate$.subscribe(function (action) {
_this.log(action);
_this.handleAction(action);
}));
this.subscription.add(reducerActions$.subscribe(function (action) {
_this.log(action);
_this.handleAction(action);
}));
this.subscription.add(immerReducerActions$.subscribe(function (action) {
_this.log(action);
_this.handleAction(action);
}));
}
Ikari.createAndBindAt = function (target, config) {
var createdIkari = this.getFrom(target);
if (createdIkari) {
return createdIkari;
}
else {
var ikari = new Ikari(target, config);
Reflect.defineMetadata(ikariSymbol, ikari, target);
return ikari;
}
};
Ikari.getFrom = function (target) {
return Reflect.getMetadata(ikariSymbol, target);
};
Ikari.prototype.destroy = function () {
this.subscription.unsubscribe();
this.triggerActions = {};
};
return Ikari;
}());
export { Ikari };
function setupEffectActions(effectActions, state$) {
var actions = {};
var effects = [];
Object.keys(effectActions).forEach(function (actionName) {
var payload$ = new Subject();
actions[actionName] = function (payload) { return payload$.next(payload); };
var effect$ = effectActions[actionName](payload$, state$);
effects.push(effect$.pipe(map(function (effectAction) { return ({
effectAction: effectAction,
originalActionName: actionName,
}); }), catchRxError()));
});
return [merge.apply(void 0, effects), actions];
}
function setupReducerActions(reducerActions, getState) {
var actions = {};
var reducers = [];
Object.keys(reducerActions).forEach(function (actionName) {
var reducer$ = new Subject();
reducers.push(reducer$);
var reducer = reducerActions[actionName];
actions[actionName] = function (params) {
var nextState = reducer(getState(), params);
reducer$.next({
reducerAction: { params: params, actionName: actionName, nextState: nextState },
originalActionName: actionName,
});
};
});
return [merge.apply(void 0, reducers), actions];
}
function setupImmerReducerActions(immerReducerActions, getState) {
var actions = {};
var immerReducers = [];
Object.keys(immerReducerActions).forEach(function (actionName) {
var immerReducer$ = new Subject();
immerReducers.push(immerReducer$);
var immerReducer = immerReducerActions[actionName];
actions[actionName] = function (params) {
var nextState = produce(getState(), function (draft) {
immerReducer(draft, params);
});
immerReducer$.next({
reducerAction: { params: params, actionName: actionName, nextState: nextState },
originalActionName: actionName,
});
};
});
return [merge.apply(void 0, immerReducers), actions];
}