gambit
Version:
A hyper-thin library to help building API driven redux apps
244 lines (182 loc) • 7.81 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _defineProperty2 = require('babel-runtime/helpers/defineProperty');
var _defineProperty3 = _interopRequireDefault(_defineProperty2);
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
exports.default = containerFactory;
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _forIn = require('lodash/forIn');
var _forIn2 = _interopRequireDefault(_forIn);
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _invariant = require('invariant');
var _invariant2 = _interopRequireDefault(_invariant);
var _containerHelpers = require('./containerHelpers');
var _dict = require('./dict');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
if (typeof document !== 'undefined') {
require('./reactErrorPatch.js');
}
function containerFactory(ComponentToWrap, _ref) {
var fetch = _ref.fetch;
var methods = _ref.methods;
var done = _ref.done;
var pending = _ref.pending;
var failed = _ref.failed;
var propTransform = _ref.propTransform;
var strictMode = _ref.strictMode;
var logging = _ref.logging;
(0, _invariant2.default)(ComponentToWrap, (0, _dict.badComponent)(fetch));
var name = ComponentToWrap.displayName;
var WrappedComponent = (0, _containerHelpers.connectComponent)(ComponentToWrap, fetch, methods, name);
var Gambit = function (_React$Component) {
(0, _inherits3.default)(Gambit, _React$Component);
function Gambit(props, context) {
(0, _classCallCheck3.default)(this, Gambit);
var _this = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(Gambit).call(this, props, context));
_this.state = {
loadingFetches: 0,
errored: false,
handlingRefreshGrab: []
};
return _this;
}
(0, _createClass3.default)(Gambit, [{
key: 'runFetches',
value: function runFetches(props, fetchesToRun) {
var _this2 = this;
var cb = arguments.length <= 2 || arguments[2] === undefined ? function () {} : arguments[2];
if (propTransform) {
props = propTransform(props);
}
var promiseObj = {};
var store = this.context.store.getState();
(0, _forIn2.default)(fetchesToRun, function (value, key) {
if (value.grab) {
var grab = void 0,
as = void 0;
try {
grab = value.grab(props.dispatch, props);
as = value.as(store, props);
} catch (e) {
console.warn('Error in ' + name, e);
}
(0, _invariant2.default)(typeof grab === 'function', (0, _dict.badGrab)(name, key, grab));
promiseObj[key] = grab(as);
}
});
var allCallsBlocked = (0, _keys2.default)(promiseObj).every(function (x) {
return !promiseObj[x];
});
if (allCallsBlocked) {
return false;
}
this.setState({
loadingFetches: this.state.loadingFetches + 1
});
return _bluebird2.default.props(promiseObj).then(function (object) {
(0, _keys2.default)(object).forEach(function (key) {
(0, _invariant2.default)(object[key] !== undefined, (0, _dict.badGrabReturn)(name, key, object[key]));
});
_this2.setState({
loadingFetches: _this2.state.loadingFetches - 1,
errored: false
}, cb);
// Workaround for https://github.com/petkaantonov/bluebird/issues/846
return null;
}).catch(function (err) {
_this2.setState({
loadingFetches: _this2.state.loadingFetches - 1,
errored: err
}, cb);
});
}
}, {
key: 'componentWillMount',
value: function componentWillMount() {
var _this3 = this;
var store = this.context.store;
this.runFetches(this.props, fetch);
// We have refreshGrabInResponse
// because when a component has a small view into a larger data set (i.e. between some dates)
// it's generally the only part of the application that knows what view it has and so
// it's best place to re-run the grab function that provides its data.
store.subscribe(function () {
var state = store.getState();
(0, _forIn2.default)(fetch, function (value, key) {
var refreshGrabInResponse = value.refreshGrabInResponse;
if (!refreshGrabInResponse) return null;
if (typeof refreshGrabInResponse === 'function') {
var parseProps = propTransform ? propTransform(_this3.props) : _this3.props;
refreshGrabInResponse = refreshGrabInResponse(state, parseProps);
}
if (refreshGrabInResponse.indexOf(state.gambit.get('lastAction')) !== -1 && _this3.state.handlingRefreshGrab.indexOf(key) === -1) {
return _this3.setState({
handlingRefreshGrab: [].concat((0, _toConsumableArray3.default)(_this3.state.handlingRefreshGrab), [key])
}, function () {
return _this3.runFetches(_this3.props, (0, _defineProperty3.default)({}, key, value), function () {
_this3.setState({
handlingRefreshGrab: _this3.state.handlingRefreshGrab.filter(function (x) {
return x !== key;
})
});
});
});
}
return null;
});
});
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
this.runFetches(nextProps, fetch);
}
}, {
key: 'render',
value: function render() {
var _state = this.state;
var loadingFetches = _state.loadingFetches;
var errored = _state.errored;
var parseProps = propTransform ? propTransform(this.props) : this.props;
if (loadingFetches !== 0 && pending) {
return pending.call(this, WrappedComponent);
} else if (loadingFetches === 0 && !errored) {
if (done) {
return done.call(this, WrappedComponent);
}
return _react2.default.createElement(WrappedComponent, parseProps);
} else if (loadingFetches === 0 && errored) {
if (failed) {
console.warn('Failed in ' + name + ': ' + errored);
return failed.call(this, WrappedComponent);
}
throw new Error(errored);
}
return null;
}
}]);
return Gambit;
}(_react2.default.Component);
Gambit.contextTypes = {
store: _react2.default.PropTypes.object
};
Gambit.displayName = 'Container(' + name + ')';
return Gambit;
}