data-provider-temporary
Version:
Library that helps with server-to-client synchronization of data
185 lines (148 loc) • 5.89 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.withRefetch = withRefetch;
exports.withDataProviders = withDataProviders;
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _DataProvider = require('./DataProvider');
var _DataProvider2 = _interopRequireDefault(_DataProvider);
var _util = require('./util');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function call(list) {
let fn = list[0];
let args = list.slice(1);
return fn(...args);
}
const idg = new _util.IdGenerator();
const dataProviders = {};
function fetch(dpRef) {
let found = null;
for (let dp of _lodash2.default.values(dataProviders)) {
if (dp.ref === dpRef) {
if (found != null) {
throw new Error(`Multiple data providers with the same ref=${dpRef}`);
}
found = dp;
}
}
if (found == null) {
throw new Error(`No data provider ref=${dpRef}`);
}
return found.fetch();
}
function withRefetch() {
return Component => {
return class ComponentWithRefetch extends _react2.default.Component {
render() {
return _react2.default.createElement(Component, _extends({
refetch: dpRef => {
// Make sure context is updated (shouldComponentUpdate of some
// parent component might prevent it from being updated)
this.forceUpdate();
fetch(dpRef);
}
}, this.props));
}
};
};
}
function withDataProviders(getConfig) {
return Component => {
var _class, _temp;
return _temp = _class = class ComponentWithDataProviders extends _react2.default.Component {
getChildContext() {
return { dataProviders: _extends({}, this.context.dataProviders, this.dataProviders) };
}
componentWillMount() {
this.id = idg.next();
this.dataProviders = {};
this.handleUpdate(this.props);
}
componentWillReceiveProps(nextProps) {
// Make sure context is updated (shouldComponentUpdate of some parent
// component might prevent it from being updated)
this.forceUpdate();
this.handleUpdate(nextProps);
}
componentWillUnmount() {
for (let dpId in this.dataProviders) {
dataProviders[dpId].removeUser(this.id);
}
}
handleUpdate(props) {
let newDataProviders = {};
for (let dpConfig of getConfig(props)) {
let {
ref,
getData: rawGetData,
onData: rawOnData,
initialData,
polling,
needed
} = dpConfig;
// Look for data provider with this ref among data providers of this
// component and data providers of its DOM ancestors
let dpId = _lodash2.default.findKey(_extends({}, this.context.dataProviders, this.dataProviders), dpRef => _lodash2.default.isEqual(dpRef, ref));
let dp;
if (dpId == null) {
(0, _util.assert)(rawOnData != null && rawGetData != null, 'Parameters onData, getData have to be provided, if data' + `provider was not defined yet. See DP ${ref}`);
dpId = idg.next();
dataProviders[dpId] = new _DataProvider2.default({
id: dpId,
ref,
rawOnData,
onData: data => {
call(rawOnData)(ref, data, this.context.dispatch);
this.forceUpdate();
},
initialData
});
}
dp = dataProviders[dpId];
// Changing onData for existing data provider is not currently
// supported
(0, _util.assert)(rawOnData == null || _lodash2.default.isEqual(rawOnData, dp.rawOnData), `Provided onData for DP ${ref}\n${rawOnData}\n` + `is not equal to previous onData\n${dp.rawOnData}`);
// If the data provider was already defined in some DOM ancestor,
// require equality on getData (i.e. there can be only one definition
// of getData for any tuple of (ref, moment in time, DOM node))
if (_lodash2.default.has(this.context.dataProviders, dpId)) {
(0, _util.assert)(rawGetData == null || _lodash2.default.isEqual(rawGetData, dp.rawGetData), `Provided getData for DP ${ref}\n${rawGetData}\n` + `is not equal to previous getData\n${dp.rawGetData}`);
}
dp.updateUser(this.id, {
polling,
needed,
rawGetData,
getData: () => call(rawGetData)
});
newDataProviders[dpId] = dp.ref;
}
for (let dpId in this.dataProviders) {
if (!_lodash2.default.has(newDataProviders, dpId)) {
dataProviders[dpId].removeUser(this.id);
}
}
this.dataProviders = newDataProviders;
this.forceUpdate();
}
render() {
let show = _lodash2.default.keys(this.dataProviders).every(id => {
let dp = dataProviders[id];
return !dp.userConfigs[this.id].needed || dp.loaded;
});
return show ? _react2.default.createElement(Component, this.props) : null;
}
}, _class.contextTypes = {
dispatch: _propTypes2.default.func.isRequired,
dataProviders: _propTypes2.default.object
}, _class.childContextTypes = {
dataProviders: _propTypes2.default.object
}, _temp;
};
}