UNPKG

react-redux-with-path

Version:

A version of react-redux designed for cases that want to access sub-tree of the state. and take it as state in mapStateToProps and mapDispatchToProps

431 lines (359 loc) 16.3 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 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.default = connect; var _react = require('react'); var _isPlainObject = require('lodash/isPlainObject'); var _isPlainObject2 = _interopRequireDefault(_isPlainObject); var _get = require('lodash/get'); var _get2 = _interopRequireDefault(_get); var _hoistNonReactStatics = require('hoist-non-react-statics'); var _hoistNonReactStatics2 = _interopRequireDefault(_hoistNonReactStatics); var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _utils = require('../utils'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 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 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'; } var errorObject = { value: null }; function tryCatch(fn, ctx) { try { return fn.apply(ctx); } catch (e) { errorObject.value = e; return errorObject; } } // 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 mapState = mapStateToProps || defaultMapStateToProps; var mapDispatch = void 0; // if (typeof mapDispatchToProps === 'function') { // mapDispatch = mapDispatchToProps // } else if (!mapDispatchToProps) { // mapDispatch = defaultMapDispatchToProps // } else { // mapDispatch = wrapActionCreators(mapDispatchToProps) // } var finalMergeProps = mergeProps || defaultMergeProps; 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; var checkMergedEquals = pure && finalMergeProps !== defaultMergeProps; // Helps track hot reloading. var version = nextVersion++; return function wrapWithConnect(WrappedComponent) { var connectDisplayName = 'Connect(' + getDisplayName(WrappedComponent) + ')'; function checkStateShape(props, methodName) { if (!(0, _isPlainObject2.default)(props)) { (0, _utils.warning)(methodName + '() in ' + connectDisplayName + ' must return a plain object. ' + ('Instead received ' + props + '.')); } } function computeMergedProps(stateProps, dispatchProps, parentProps) { var mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps); if (process.env.NODE_ENV !== 'production') { checkStateShape(mergedProps, 'mergeProps'); } return mergedProps; } var Connect = function (_Component) { _inherits(Connect, _Component); _createClass(Connect, [{ key: 'shouldComponentUpdate', value: function shouldComponentUpdate() { return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged; } }]); function Connect(props, context) { _classCallCheck(this, Connect); var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Connect).call(this, props, context)); _this.version = version; _this.store = props.store || context.store; _this.path = props.path || context.path; (0, _invariant2.default)(_this.store, 'Could not find "store" in either the context or ' + ('props of "' + connectDisplayName + '". ') + 'Either wrap the root component in a <Provider>, ' + ('or explicitly pass "store" as a prop to "' + connectDisplayName + '".')); var storeState = _this.getSubstate(); _this.state = { storeState: storeState }; _this.clearCache(); return _this; } _createClass(Connect, [{ key: 'computeStateProps', value: function computeStateProps(store, props) { if (!this.finalMapStateToProps) { return this.configureFinalMapState(store, props); } var state = this.getSubstate(); var stateProps = this.doStatePropsDependOnOwnProps ? this.finalMapStateToProps(state, props) : this.finalMapStateToProps(state); if (process.env.NODE_ENV !== 'production') { checkStateShape(stateProps, 'mapStateToProps'); } return stateProps; } }, { key: 'configureFinalMapState', value: function configureFinalMapState(store, props) { var mappedState = mapState(this.getSubstate(), props); var isFactory = typeof mappedState === 'function'; this.finalMapStateToProps = isFactory ? mappedState : mapState; this.doStatePropsDependOnOwnProps = this.finalMapStateToProps.length !== 1; if (isFactory) { return this.computeStateProps(store, props); } if (process.env.NODE_ENV !== 'production') { checkStateShape(mappedState, 'mapStateToProps'); } return mappedState; } }, { key: 'computeDispatchProps', value: function computeDispatchProps(store, props) { if (!this.finalMapDispatchToProps) { return this.configureFinalMapDispatch(store, props); } var dispatch = store.dispatch; var dispatchProps = this.doDispatchPropsDependOnOwnProps ? this.finalMapDispatchToProps(dispatch, props) : this.finalMapDispatchToProps(dispatch); if (process.env.NODE_ENV !== 'production') { checkStateShape(dispatchProps, 'mapDispatchToProps'); } return dispatchProps; } }, { key: 'configureFinalMapDispatch', value: function configureFinalMapDispatch(store, props) { // move from line 42 to here because we need the path now: start if (typeof mapDispatchToProps === 'function') { mapDispatch = mapDispatchToProps; } else if (!mapDispatchToProps) { mapDispatch = defaultMapDispatchToProps; } else { mapDispatch = (0, _utils.wrapActionCreators)(mapDispatchToProps, this.path); } var mappedDispatch = mapDispatch(store.dispatch, props); // end var isFactory = typeof mappedDispatch === 'function'; this.finalMapDispatchToProps = isFactory ? mappedDispatch : mapDispatch; this.doDispatchPropsDependOnOwnProps = this.finalMapDispatchToProps.length !== 1; if (isFactory) { return this.computeDispatchProps(store, props); } if (process.env.NODE_ENV !== 'production') { checkStateShape(mappedDispatch, 'mapDispatchToProps'); } return mappedDispatch; } }, { key: 'updateStatePropsIfNeeded', value: function updateStatePropsIfNeeded() { var nextStateProps = this.computeStateProps(this.store, this.props); if (this.stateProps && (0, _utils.shallowEqual)(nextStateProps, this.stateProps)) { return false; } this.stateProps = nextStateProps; return true; } }, { key: 'updateDispatchPropsIfNeeded', value: function updateDispatchPropsIfNeeded() { var nextDispatchProps = this.computeDispatchProps(this.store, this.props); if (this.dispatchProps && (0, _utils.shallowEqual)(nextDispatchProps, this.dispatchProps)) { return false; } this.dispatchProps = nextDispatchProps; return true; } }, { key: 'updateMergedPropsIfNeeded', value: function updateMergedPropsIfNeeded() { var nextMergedProps = computeMergedProps(this.stateProps, this.dispatchProps, this.props); if (this.mergedProps && checkMergedEquals && (0, _utils.shallowEqual)(nextMergedProps, this.mergedProps)) { return false; } this.mergedProps = nextMergedProps; return true; } }, { key: 'isSubscribed', value: function isSubscribed() { return typeof this.unsubscribe === 'function'; } }, { key: 'trySubscribe', value: function trySubscribe() { if (shouldSubscribe && !this.unsubscribe) { this.unsubscribe = this.store.subscribe(this.handleChange.bind(this)); this.handleChange(); } } }, { key: 'tryUnsubscribe', value: function tryUnsubscribe() { if (this.unsubscribe) { this.unsubscribe(); this.unsubscribe = null; } } }, { key: 'componentDidMount', value: function componentDidMount() { this.trySubscribe(); } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { if (!pure || !(0, _utils.shallowEqual)(nextProps, this.props)) { this.haveOwnPropsChanged = true; } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this.tryUnsubscribe(); this.clearCache(); } }, { key: 'clearCache', value: function clearCache() { this.dispatchProps = null; this.stateProps = null; this.mergedProps = null; this.haveOwnPropsChanged = true; this.hasStoreStateChanged = true; this.haveStatePropsBeenPrecalculated = false; this.statePropsPrecalculationError = null; this.renderedElement = null; this.finalMapDispatchToProps = null; this.finalMapStateToProps = null; } }, { key: 'handleChange', value: function handleChange() { if (!this.unsubscribe) { return; } var storeState = this.getSubstate(); var prevStoreState = this.state.storeState; if (pure && prevStoreState === storeState) { return; } if (pure && !this.doStatePropsDependOnOwnProps) { var haveStatePropsChanged = tryCatch(this.updateStatePropsIfNeeded, this); if (!haveStatePropsChanged) { return; } if (haveStatePropsChanged === errorObject) { this.statePropsPrecalculationError = errorObject.value; } this.haveStatePropsBeenPrecalculated = true; } this.hasStoreStateChanged = true; this.setState({ storeState: storeState }); } }, { key: 'getWrappedInstance', value: function getWrappedInstance() { (0, _invariant2.default)(withRef, 'To access the wrapped instance, you need to specify ' + '{ withRef: true } as the fourth argument of the connect() call.'); return this.refs.wrappedInstance; } }, { key: 'getSubstate', value: function getSubstate() { if (!this.path) { return this.store.getState(); } return (0, _get2.default)(this.store.getState(), this.path); } }, { key: 'render', value: function render() { var haveOwnPropsChanged = this.haveOwnPropsChanged; var hasStoreStateChanged = this.hasStoreStateChanged; var haveStatePropsBeenPrecalculated = this.haveStatePropsBeenPrecalculated; var statePropsPrecalculationError = this.statePropsPrecalculationError; var renderedElement = this.renderedElement; this.haveOwnPropsChanged = false; this.hasStoreStateChanged = false; this.haveStatePropsBeenPrecalculated = false; this.statePropsPrecalculationError = null; if (statePropsPrecalculationError) { throw statePropsPrecalculationError; } var shouldUpdateStateProps = true; var shouldUpdateDispatchProps = true; if (pure && renderedElement) { shouldUpdateStateProps = hasStoreStateChanged || haveOwnPropsChanged && this.doStatePropsDependOnOwnProps; shouldUpdateDispatchProps = haveOwnPropsChanged && this.doDispatchPropsDependOnOwnProps; } var haveStatePropsChanged = false; var haveDispatchPropsChanged = false; if (haveStatePropsBeenPrecalculated) { haveStatePropsChanged = true; } else if (shouldUpdateStateProps) { haveStatePropsChanged = this.updateStatePropsIfNeeded(); } if (shouldUpdateDispatchProps) { haveDispatchPropsChanged = this.updateDispatchPropsIfNeeded(); } var haveMergedPropsChanged = true; if (haveStatePropsChanged || haveDispatchPropsChanged || haveOwnPropsChanged) { haveMergedPropsChanged = this.updateMergedPropsIfNeeded(); } else { haveMergedPropsChanged = false; } if (!haveMergedPropsChanged && renderedElement) { return renderedElement; } if (withRef) { this.renderedElement = (0, _react.createElement)(WrappedComponent, _extends({}, this.mergedProps, { ref: 'wrappedInstance' })); } else { this.renderedElement = (0, _react.createElement)(WrappedComponent, this.mergedProps); } return this.renderedElement; } }]); return Connect; }(_react.Component); Connect.displayName = connectDisplayName; Connect.WrappedComponent = WrappedComponent; Connect.contextTypes = { path: _utils.pathType, store: _utils.storeShape }; Connect.propTypes = { store: _utils.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 (0, _hoistNonReactStatics2.default)(Connect, WrappedComponent); }; }