@graphistry/falcor-react-redux
Version:
Falcor + React + Redux = ❤️
493 lines (394 loc) • 19.6 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.container = undefined;
var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');
var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
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 _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _falcor = require('@graphistry/falcor');
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _invariant = require('invariant');
var _invariant2 = _interopRequireDefault(_invariant);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _hoistStatics = require('recompose/hoistStatics');
var _hoistStatics2 = _interopRequireDefault(_hoistStatics);
var _shallowEqual = require('recompose/shallowEqual');
var _shallowEqual2 = _interopRequireDefault(_shallowEqual);
var _wrapDisplayName = require('recompose/wrapDisplayName');
var _wrapDisplayName2 = _interopRequireDefault(_wrapDisplayName);
var _fetchDataUntilSettled = require('../utils/fetchDataUntilSettled');
var _fetchDataUntilSettled2 = _interopRequireDefault(_fetchDataUntilSettled);
var _Subject = require('rxjs/Subject');
var _Observable = require('rxjs/Observable');
require('rxjs/add/observable/of');
require('rxjs/add/operator/takeLast');
require('rxjs/add/operator/switchMap');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var defaultMapFragmentToProps = function defaultMapFragmentToProps(data) {
return data;
};
var defaultMapDispatchToProps = function defaultMapDispatchToProps(dispatch, props, falcor) {
return {};
};
var defaultMergeProps = function defaultMergeProps(stateProps, dispatchProps, parentProps) {
return (0, _extends3.default)({}, parentProps, stateProps, dispatchProps);
};
exports.container = container;
exports.default = container;
container.globalDisposeDelay = 0;
container.globalDisposeScheduler = null;
function container(fragmentDesc) {
(0, _invariant2.default)(fragmentDesc && ('function' === typeof fragmentDesc || 'object' === (typeof fragmentDesc === 'undefined' ? 'undefined' : (0, _typeof3.default)(fragmentDesc)) && 'function' === typeof fragmentDesc.fragment), 'Attempted to create a Falcor container component without a fragment.\nFalcor containers must be created with a fragment function, or an Object with a "fragment" function.');
var renderErrors = false,
renderLoading = false,
fragment = void 0,
mapFragment = void 0,
disposeScheduler = void 0,
disposeDelay = void 0,
mapDispatch = void 0,
mapFragmentAndProps = void 0;
if ('object' !== (typeof fragmentDesc === 'undefined' ? 'undefined' : (0, _typeof3.default)(fragmentDesc))) {
fragment = fragmentDesc;
mapFragment = arguments.length <= 1 ? undefined : arguments[1];
mapDispatch = arguments.length <= 2 ? undefined : arguments[2];
mapFragmentAndProps = arguments.length <= 3 ? undefined : arguments[3];
} else {
fragment = fragmentDesc.fragment;
mapFragment = fragmentDesc.mapFragment;
renderErrors = fragmentDesc.renderErrors;
renderLoading = fragmentDesc.renderLoading;
disposeDelay = fragmentDesc.disposeDelay;
disposeScheduler = fragmentDesc.disposeScheduler;
mapFragmentAndProps = fragmentDesc.mapFragmentAndProps;
mapDispatch = fragmentDesc.mapDispatch || fragmentDesc.dispatchers;
}
mapFragment = mapFragment || defaultMapFragmentToProps;
mapDispatch = mapDispatch || defaultMapDispatchToProps;
mapFragmentAndProps = mapFragmentAndProps || defaultMergeProps;
disposeDelay = disposeDelay || container.globalDisposeDelay;
disposeScheduler = disposeScheduler || container.globalDisposeScheduler;
if ('function' !== typeof mapDispatch) {
if (mapDispatch && 'object' !== (typeof mapDispatch === 'undefined' ? 'undefined' : (0, _typeof3.default)(mapDispatch))) {
mapDispatch = defaultMapDispatchToProps;
} else {
mapDispatch = bindActionCreators(mapDispatch);
}
}
return (0, _hoistStatics2.default)(function (Component) {
var _class, _temp;
return _temp = _class = function (_FalcorContainer) {
(0, _inherits3.default)(Container, _FalcorContainer);
function Container(props, context) {
(0, _classCallCheck3.default)(this, Container);
var _this = (0, _possibleConstructorReturn3.default)(this, (Container.__proto__ || (0, _getPrototypeOf2.default)(Container)).call(this, props, context));
_this.fragment = fragment;
_this.Component = Component;
_this.mapFragment = mapFragment;
_this.renderErrors = renderErrors;
_this.renderLoading = renderLoading;
_this.dispatchers = mapDispatch(_this);
_this.mapFragmentAndProps = mapFragmentAndProps;
_this.disposeDelay = disposeDelay;
_this.disposeScheduler = disposeScheduler;
return _this;
}
return Container;
}(FalcorContainer), _class.fragment = fragment, _class.fragments = fragments, _class.load = fetchEachPropUpdate, _class.contextTypes = contextTypes, _class.childContextTypes = contextTypes, _class.displayName = (0, _wrapDisplayName2.default)(Component, 'Container'), _temp;
});
}
var fragments = function fragments(items, itemsProps) {
if (!items || 'object' !== (typeof items === 'undefined' ? 'undefined' : (0, _typeof3.default)(items))) {
return '{ length }';
}
itemsProps = !itemsProps || 'object' !== (typeof itemsProps === 'undefined' ? 'undefined' : (0, _typeof3.default)(itemsProps)) ? [] : itemsProps;
var index = -1,
query = 'length';
var length = Math.max(0, items.length) || 0;
while (++index < length) {
query = query + ',\n ' + index + ': ' + this.fragment(items[index], itemsProps[index]);
}
return '{ ' + query + ' }';
};
function bindActionCreators(actionCreators) {
return function mapDispatch(container) {
return (0, _keys2.default)(actionCreators).reduce(function (dispatchers, key) {
var actionCreator = actionCreators[key];
dispatchers[key] = function () {
var _container$state = container.state,
falcor = _container$state.falcor,
dispatch = _container$state.dispatch;
if (falcor) {
return dispatch((0, _extends3.default)({ falcor: falcor }, actionCreator.apply(undefined, arguments)));
}
};
return dispatchers;
}, {});
};
}
function tryDeref(_ref) {
var data = _ref.data,
falcor = _ref.falcor;
return !data || !falcor ? falcor : falcor._hasValidParentReference() ? falcor.deref(data) : null;
}
function fetchEachPropUpdate(update) {
(0, _invariant2.default)(update.fragment || (update.fragment = this.fragment), 'Attempted to fetch without a fragment definition');
if (!(update.falcor = tryDeref(update))) {
return _Observable.Observable.of(update);
} else if (update.renderLoading === true) {
return (0, _fetchDataUntilSettled2.default)(update);
} else {
return (0, _fetchDataUntilSettled2.default)(update).takeLast(1);
}
}
function mergeEachPropUpdate(_ref2, _ref3) {
var props = _ref2.props,
falcor = _ref2.falcor,
dispatch = _ref2.dispatch;
var data = _ref3.data,
query = _ref3.query,
error = _ref3.error,
version = _ref3.version,
loading = _ref3.loading;
var hash = data && data.$__hash;
var status = data && data.$__status;
loading = status === 'pending';
return {
hash: hash, props: props, falcor: falcor, dispatch: dispatch,
data: data, query: query, error: error, loading: loading, version: version
};
}
var contextTypes = {
falcor: _propTypes2.default.object,
dispatch: _propTypes2.default.func
};
var FalcorContainer = function (_React$Component) {
(0, _inherits3.default)(FalcorContainer, _React$Component);
function FalcorContainer(componentProps, context) {
(0, _classCallCheck3.default)(this, FalcorContainer);
var _this2 = (0, _possibleConstructorReturn3.default)(this, (FalcorContainer.__proto__ || (0, _getPrototypeOf2.default)(FalcorContainer)).call(this, componentProps, context));
var falcor = context.falcor;
var data = componentProps.data,
props = (0, _objectWithoutProperties3.default)(componentProps, ['data']);
_this2.propsStream = new _Subject.Subject();
_this2.propsAction = _this2.propsStream.switchMap(fetchEachPropUpdate, mergeEachPropUpdate);
_this2.state = {
data: data, props: props, query: null,
dispatch: context.dispatch,
falcor: tryDeref({ data: data, falcor: falcor })
};
return _this2;
}
(0, _createClass3.default)(FalcorContainer, [{
key: 'getChildContext',
value: function getChildContext() {
var _state = this.state,
falcor = _state.falcor,
dispatch = _state.dispatch;
return { falcor: falcor, dispatch: dispatch };
}
}, {
key: 'shouldComponentUpdate',
value: function shouldComponentUpdate(nextProps, nextState, nextContext) {
var renderLoading = this.renderLoading,
_props = this.props,
currProps = _props === undefined ? {} : _props,
_state2 = this.state,
currState = _state2 === undefined ? {} : _state2;
if (renderLoading === true && currState.loading !== nextState.loading) {
this.guardTraceShouldUpdate() && this.traceShouldUpdate('trigger: loading', { curr: currState.loading, next: nextState.loading });
return true;
} else if (currState.version !== nextState.version) {
this.guardTraceShouldUpdate() && this.traceShouldUpdate('trigger: version', { curr: currState.version, next: nextState.version });
return true;
} else if (currState.error !== nextState.error) {
this.guardTraceShouldUpdate() && this.traceShouldUpdate('trigger: error', { curr: currState.error, next: nextState.error });
return true;
} else if (currState.hash !== nextState.hash) {
this.guardTraceShouldUpdate() && this.traceShouldUpdate('trigger: hash', { curr: currState.hash, next: nextState.hash });
return true;
}
var currData = currProps.data,
_currProps$style = currProps.style,
currStyle = _currProps$style === undefined ? {} : _currProps$style,
restCurrProps = (0, _objectWithoutProperties3.default)(currProps, ['data', 'style']);
var nextData = nextProps.data,
_nextProps$style = nextProps.style,
nextStyle = _nextProps$style === undefined ? currStyle : _nextProps$style,
restNextProps = (0, _objectWithoutProperties3.default)(nextProps, ['data', 'style']);
if (!(0, _shallowEqual2.default)(currData, nextData)) {
this.guardTraceShouldUpdate() && this.traceShouldUpdate('trigger: data', { currData: (0, _falcor.toProps)(currData), nextData: (0, _falcor.toProps)(nextData) });
return true;
} else if (!(0, _shallowEqual2.default)(currStyle, nextStyle)) {
this.guardTraceShouldUpdate() && this.traceShouldUpdate('trigger: style', { currStyle: currStyle, nextStyle: nextStyle });
return true;
} else if (!(0, _shallowEqual2.default)(restCurrProps, restNextProps)) {
this.guardTraceShouldUpdate() && this.traceShouldUpdate('trigger: props', { restCurrProps: restCurrProps, restNextProps: restNextProps });
return true;
}
// this.guardTraceShouldUpdate() && this.traceShouldUpdate(false,
// { currProps: serializeObjectWithFalcorData(currProps), nextProps: serializeObjectWithFalcorData(nextProps) },
// { currState: serializeObjectWithFalcorData(currState), nextState: serializeObjectWithFalcorData(nextState) }
// );
return false;
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps, nextContext) {
// Receive new props from the owner
var data = nextProps.data,
props = (0, _objectWithoutProperties3.default)(nextProps, ['data']);
this.propsStream.next({
data: data, props: props,
query: this.state.query,
fragment: this.fragment,
falcor: nextContext.falcor,
version: this.state.version,
dispatch: nextContext.dispatch,
renderLoading: this.renderLoading,
disposeDelay: this.disposeDelay,
disposeScheduler: this.disposeScheduler
});
}
}, {
key: 'componentWillMount',
value: function componentWillMount() {
var _props2 = this.props,
data = _props2.data,
props = (0, _objectWithoutProperties3.default)(_props2, ['data']);
// Subscribe to child prop changes so we know when to re-render
this.propsSubscription = this.propsAction.subscribe(this.setState.bind(this));
this.propsStream.next({
data: data, props: props,
query: this.state.query,
fragment: this.fragment,
falcor: this.context.falcor,
version: this.state.version,
dispatch: this.context.dispatch,
renderLoading: this.renderLoading,
disposeDelay: this.disposeDelay,
disposeScheduler: this.disposeScheduler
});
}
}, {
key: 'componentWillUpdate',
value: function componentWillUpdate(nextProps, nextState) {
this.guardTraceWillUpdate() && this.traceWillUpdate('loading: ' + nextState.loading || false, { currProps: serializeObjectWithFalcorData(this.props), nextProps: serializeObjectWithFalcorData(nextProps) }, { currState: serializeObjectWithFalcorData(this.state), nextState: serializeObjectWithFalcorData(nextState) });
}
}, {
key: 'traceShouldUpdate',
value: function traceShouldUpdate() {
if (this.guardTraceShouldUpdate()) {
console.group('should update: ' + this.inspect());
for (var _len = arguments.length, message = Array(_len), _key = 0; _key < _len; _key++) {
message[_key] = arguments[_key];
}
message.forEach(function (x) {
return console.log(x);
});
console.groupEnd();
}
}
}, {
key: 'traceWillUpdate',
value: function traceWillUpdate() {
if (this.guardTraceWillUpdate()) {
console.group(' will update: ' + this.inspect());
for (var _len2 = arguments.length, message = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
message[_key2] = arguments[_key2];
}
message.forEach(function (x) {
return console.log(x);
});
console.groupEnd();
}
}
}, {
key: 'guardTraceShouldUpdate',
value: function guardTraceShouldUpdate() {
return !!global['__trace_container_diffs__'];
}
}, {
key: 'guardTraceWillUpdate',
value: function guardTraceWillUpdate() {
return !!global['__trace_container_updates__'];
}
}, {
key: 'inspect',
value: function inspect() {
var _state3 = this.state,
state = _state3 === undefined ? {} : _state3;
var falcor = state.falcor;
var name = this.constructor && this.constructor.displayName || 'Unknown Component';
return name + ': ' + (falcor && falcor.inspect() || '{ v: -1, p: [] }');
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
// Clean-up subscription before un-mounting
this.propsSubscription.unsubscribe();
this.propsSubscription = undefined;
this.propsStream = undefined;
this.propsAction = undefined;
this.fragment = null;
this.Component = null;
this.dispatchers = null;
this.mapDispatch = null;
this.mapFragment = null;
this.renderLoading = null;
this.mergeFragmentAndProps = null;
}
}, {
key: 'render',
value: function render() {
var renderErrors = this.renderErrors,
renderLoading = this.renderLoading,
mapFragment = this.mapFragment,
mapFragmentAndProps = this.mapFragmentAndProps,
Component = this.Component,
dispatchers = this.dispatchers,
state = this.state;
if (!Component) {
return null;
}
var data = state.data,
props = state.props,
error = state.error,
falcor = state.falcor;
var mappedFragment = mapFragment(data || [], props, falcor);
var allMergedProps = mapFragmentAndProps(mappedFragment, dispatchers, props);
if (error && renderErrors === true) {
allMergedProps.error = error;
}
if (renderLoading === true) {
allMergedProps.loading = state.loading || false;
}
return _react2.default.createElement(Component, allMergedProps);
}
}]);
return FalcorContainer;
}(_react2.default.Component);
function serializeObjectWithFalcorData(obj) {
if (obj && (typeof obj === 'undefined' ? 'undefined' : (0, _typeof3.default)(obj)) === 'object' && obj.data) {
return (0, _extends3.default)({}, obj, { data: (0, _falcor.toProps)(obj.data) });
}
return obj;
}
//# sourceMappingURL=container.js.map