@xailabs/altx
Version:
Flux flavor based on alt.js
95 lines (78 loc) • 3.74 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = bindHandlers;
var _decorators = require('alt-utils/lib/decorators');
var _flatten = require('../utils/flatten');
var _flatten2 = _interopRequireDefault(_flatten);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// TODO clean up and refactor terminoloy, potentially deprecate the old "handlers" system
// (viewActions are now called handlers, e.g. handlerFactory)
/**
* Decorates a store with any number of action handlers.
*/
function bindHandlers(actions) {
for (var _len = arguments.length, handlers = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
handlers[_key - 1] = arguments[_key];
}
return function decorateStore(storeClass) {
// attach bound handler methods to the store class for each handler definition
(0, _flatten2.default)(handlers).forEach(function (handler) {
return attachBoundHandler(storeClass, actions[handler.name], handler);
});
return storeClass;
};
}
/**
* Attaches a single reducer handling to the store.
*
* A new handler method will be created on the store class.
* The handler method invokes the reducer giving it the current store state.
* It then sets the reducer result as the new store state.
*
* If a sideEffect function is defined, it will be called with the signature `{state, prevState, payload}`
*
* @param {object} storeClass - The class of the store to be decorated
* @param {object} action - An altjs action
* @param {object} handler - An handler object with `{name[, reducer, sideEffect]}`
*/
function attachBoundHandler(storeClass, action, handler) {
// name is required
var methodName = '__handle_' + handler.name;
if (storeClass.prototype[methodName]) throw new Error('Duplicate method "' + methodName + '"');
/**
* Handles an action call and sets the next state of the store.
*
* @param {any} payload - the single argument that can be specified when calling an action.
* If you need to use more than one argument, use an object with any properties you need.
*/
storeClass.prototype[methodName] = function handleAction(payload) {
var reducer = handler.hasOwnProperty('reducer') && handler.reducer;
var sideEffect = handler.hasOwnProperty('sideEffect') && handler.sideEffect;
var currentState = this.state;
// the actual operation: run the reducer and set its result as state
var nextState = currentState;
if (reducer) {
try {
nextState = reducer(currentState, payload);
} catch (error) {
console.error('Error in reducer (' + handler.name + ', ' + handler.name + ')', error);
}
}
if (nextState) {
this.setState(nextState);
} else if (reducer) {
console.warn('reducer "' + handler.name + '" in call "' + handler.name + '" did not return a new state.\n Either you forgot to return it, or if no state change is required, maybe you should use a sideEffect instead of a reducer.\n ');
}
if (sideEffect) {
try {
sideEffect({ state: nextState, prevState: currentState, payload: payload });
} catch (error) {
console.error('Error in sideEffect (' + handler.name + ', ' + handler.name + ')', error);
}
}
};
var bindhandler = (0, _decorators.bind)(action);
bindhandler(storeClass, methodName, Object.getOwnPropertyDescriptor(storeClass.prototype, methodName));
};