Skip to content

Instantly share code, notes, and snippets.

@tmcw
Created January 27, 2016 21:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmcw/b844e504632df5742e6a to your computer and use it in GitHub Desktop.
Save tmcw/b844e504632df5742e6a to your computer and use it in GitHub Desktop.
'use strict';
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; };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _require = require('react');
var Component = _require.Component;
var createElement = _require.createElement;
var storeShape = require('../utils/storeShape');
var shallowEqual = require('../utils/shallowEqual');
var isPlainObject = require('../utils/isPlainObject');
var wrapActionCreators = require('../utils/wrapActionCreators');
var hoistStatics = require('hoist-non-react-statics');
var invariant = require('invariant');
var defaultMapStateToProps = function defaultMapStateToProps(state) {
return {};
}; // eslint-disable-line no-unused-vars
var defaultMapDispatchToProps = function defaultMapDispatchToProps(dispatch) {
return { dispatch: dispatch };
};
var defaultMergeProps = function defaultMergeProps(stateProps, dispatchProps, parentProps) {
return _extends({}, parentProps, stateProps, dispatchProps);
};
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
// Helps track hot reloading.
var nextVersion = 0;
function connect(mapStateToProps, mapDispatchToProps, mergeProps) {
var options = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3];
var shouldSubscribe = Boolean(mapStateToProps);
var finalMapStateToProps = mapStateToProps || defaultMapStateToProps;
var finalMapDispatchToProps = isPlainObject(mapDispatchToProps) ? wrapActionCreators(mapDispatchToProps) : mapDispatchToProps || defaultMapDispatchToProps;
var finalMergeProps = mergeProps || defaultMergeProps;
var doStatePropsDependOnOwnProps = finalMapStateToProps.length !== 1;
var doDispatchPropsDependOnOwnProps = finalMapDispatchToProps.length !== 1;
var _options$pure = options.pure;
var pure = _options$pure === undefined ? true : _options$pure;
var _options$withRef = options.withRef;
var withRef = _options$withRef === undefined ? false : _options$withRef;
// Helps track hot reloading.
var version = nextVersion++;
function computeStateProps(store, props) {
var state = store.getState();
var stateProps = doStatePropsDependOnOwnProps ? finalMapStateToProps(state, props) : finalMapStateToProps(state);
invariant(isPlainObject(stateProps), '`mapStateToProps` must return an object. Instead received %s.', stateProps);
return stateProps;
}
function computeDispatchProps(store, props) {
var dispatch = store.dispatch;
var dispatchProps = doDispatchPropsDependOnOwnProps ? finalMapDispatchToProps(dispatch, props) : finalMapDispatchToProps(dispatch);
invariant(isPlainObject(dispatchProps), '`mapDispatchToProps` must return an object. Instead received %s.', dispatchProps);
return dispatchProps;
}
function computeMergedProps(stateProps, dispatchProps, parentProps) {
var mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps);
invariant(isPlainObject(mergedProps), '`mergeProps` must return an object. Instead received %s.', mergedProps);
return mergedProps;
}
return function wrapWithConnect(WrappedComponent) {
var Connect = (function (_Component) {
_inherits(Connect, _Component);
Connect.prototype.shouldComponentUpdate = function shouldComponentUpdate() {
return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged;
};
function Connect(props, context) {
_classCallCheck(this, Connect);
var _this = _possibleConstructorReturn(this, _Component.call(this, props, context));
_this.version = version;
_this.store = props.store || context.store;
invariant(_this.store, 'Could not find "store" in either the context or ' + ('props of "' + _this.constructor.displayName + '". ') + 'Either wrap the root component in a <Provider>, ' + ('or explicitly pass "store" as a prop to "' + _this.constructor.displayName + '".'));
var storeState = _this.store.getState();
_this.state = { storeState: storeState };
_this.clearCache();
return _this;
}
Connect.prototype.updateStatePropsIfNeeded = function updateStatePropsIfNeeded() {
var nextStateProps = computeStateProps(this.store, this.props);
if (this.stateProps && shallowEqual(nextStateProps, this.stateProps)) {
return false;
}
this.stateProps = nextStateProps;
return true;
};
Connect.prototype.updateDispatchPropsIfNeeded = function updateDispatchPropsIfNeeded() {
var nextDispatchProps = computeDispatchProps(this.store, this.props);
if (this.dispatchProps && shallowEqual(nextDispatchProps, this.dispatchProps)) {
return false;
}
this.dispatchProps = nextDispatchProps;
return true;
};
Connect.prototype.updateMergedProps = function updateMergedProps() {
this.mergedProps = computeMergedProps(this.stateProps, this.dispatchProps, this.props);
};
Connect.prototype.isSubscribed = function isSubscribed() {
return typeof this.unsubscribe === 'function';
};
Connect.prototype.trySubscribe = function trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe = this.store.subscribe(this.handleChange.bind(this));
this.handleChange();
}
};
Connect.prototype.tryUnsubscribe = function tryUnsubscribe() {
if (this.unsubscribe) {
this.unsubscribe();
this.unsubscribe = null;
}
};
Connect.prototype.componentDidMount = function componentDidMount() {
this.trySubscribe();
};
Connect.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
if (!pure || !shallowEqual(nextProps, this.props)) {
console.log(shallowEqual(nextProps,
this.props, true), 'in', Connect.displayName);
this.haveOwnPropsChanged = true;
}
};
Connect.prototype.componentWillUnmount = function componentWillUnmount() {
this.tryUnsubscribe();
this.clearCache();
};
Connect.prototype.clearCache = function clearCache() {
this.dispatchProps = null;
this.stateProps = null;
this.mergedProps = null;
this.haveOwnPropsChanged = true;
this.hasStoreStateChanged = true;
this.renderedElement = null;
};
Connect.prototype.handleChange = function handleChange() {
if (!this.unsubscribe) {
return;
}
var prevStoreState = this.state.storeState;
var storeState = this.store.getState();
if (!pure || prevStoreState !== storeState) {
this.hasStoreStateChanged = true;
this.setState({ storeState: storeState });
}
};
Connect.prototype.getWrappedInstance = function getWrappedInstance() {
invariant(withRef, 'To access the wrapped instance, you need to specify ' + '{ withRef: true } as the fourth argument of the connect() call.');
return this.refs.wrappedInstance;
};
Connect.prototype.render = function render() {
var haveOwnPropsChanged = this.haveOwnPropsChanged;
var hasStoreStateChanged = this.hasStoreStateChanged;
var renderedElement = this.renderedElement;
this.haveOwnPropsChanged = false;
this.hasStoreStateChanged = false;
var shouldUpdateStateProps = true;
var shouldUpdateDispatchProps = true;
if (pure && renderedElement) {
shouldUpdateStateProps = hasStoreStateChanged || haveOwnPropsChanged && doStatePropsDependOnOwnProps;
shouldUpdateDispatchProps = haveOwnPropsChanged && doDispatchPropsDependOnOwnProps;
}
var haveStatePropsChanged = false;
var haveDispatchPropsChanged = false;
if (shouldUpdateStateProps) {
haveStatePropsChanged = this.updateStatePropsIfNeeded();
}
if (shouldUpdateDispatchProps) {
haveDispatchPropsChanged = this.updateDispatchPropsIfNeeded();
}
var haveMergedPropsChanged = true;
if (haveStatePropsChanged || haveDispatchPropsChanged || haveOwnPropsChanged) {
this.updateMergedProps();
} else {
haveMergedPropsChanged = false;
}
if (!haveMergedPropsChanged && renderedElement) {
return renderedElement;
}
if (withRef) {
this.renderedElement = createElement(WrappedComponent, _extends({}, this.mergedProps, {
ref: 'wrappedInstance'
}));
} else {
this.renderedElement = createElement(WrappedComponent, this.mergedProps);
}
return this.renderedElement;
};
return Connect;
})(Component);
Connect.displayName = 'Connect(' + getDisplayName(WrappedComponent) + ')';
Connect.WrappedComponent = WrappedComponent;
Connect.contextTypes = {
store: storeShape
};
Connect.propTypes = {
store: storeShape
};
if (process.env.NODE_ENV !== 'production') {
Connect.prototype.componentWillUpdate = function componentWillUpdate() {
if (this.version === version) {
return;
}
// We are hot reloading!
this.version = version;
this.trySubscribe();
this.clearCache();
};
}
return hoistStatics(Connect, WrappedComponent);
};
}
module.exports = connect;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment