react-redux
Advanced tools
Comparing version 1.0.0-alpha to 1.0.0
@@ -1,6 +0,1 @@ | ||
# Change log | ||
All notable changes to this project will be documented in this file. | ||
This project adheres to [Semantic Versioning](http://semver.org/). | ||
TODO | ||
All notable changes are described on the [Releases](https://github.com/gaearon/react-redux/releases) page. |
(function webpackUniversalModuleDefinition(root, factory) { | ||
if(typeof exports === 'object' && typeof module === 'object') | ||
module.exports = factory(require("react")); | ||
module.exports = factory(require("react"), require("redux")); | ||
else if(typeof define === 'function' && define.amd) | ||
define(["react"], factory); | ||
define(["react", "redux"], factory); | ||
else if(typeof exports === 'object') | ||
exports["ReduxReact"] = factory(require("react")); | ||
exports["ReactRedux"] = factory(require("react"), require("redux")); | ||
else | ||
root["ReduxReact"] = factory(root["React"]); | ||
})(this, function(__WEBPACK_EXTERNAL_MODULE_12__) { | ||
root["ReactRedux"] = factory(root["React"], root["Redux"]); | ||
})(this, function(__WEBPACK_EXTERNAL_MODULE_10__, __WEBPACK_EXTERNAL_MODULE_11__) { | ||
return /******/ (function(modules) { // webpackBootstrap | ||
@@ -63,7 +63,7 @@ /******/ // The module cache | ||
var _react = __webpack_require__(12); | ||
var _react = __webpack_require__(10); | ||
var _react2 = _interopRequireDefault(_react); | ||
var _componentsCreateAll = __webpack_require__(3); | ||
var _componentsCreateAll = __webpack_require__(2); | ||
@@ -75,8 +75,4 @@ var _componentsCreateAll2 = _interopRequireDefault(_componentsCreateAll); | ||
var Provider = _createAll.Provider; | ||
var Connector = _createAll.Connector; | ||
var provide = _createAll.provide; | ||
var connect = _createAll.connect; | ||
exports.Provider = Provider; | ||
exports.Connector = Connector; | ||
exports.provide = provide; | ||
exports.connect = connect; | ||
@@ -105,17 +101,2 @@ | ||
/* 2 */ | ||
/***/ function(module, exports) { | ||
'use strict'; | ||
exports.__esModule = true; | ||
exports['default'] = getDisplayName; | ||
function getDisplayName(Component) { | ||
return Component.displayName || Component.name || 'Component'; | ||
} | ||
module.exports = exports['default']; | ||
/***/ }, | ||
/* 3 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -130,28 +111,15 @@ | ||
var _createProvider = __webpack_require__(7); | ||
var _createProvider = __webpack_require__(4); | ||
var _createProvider2 = _interopRequireDefault(_createProvider); | ||
var _createProvideDecorator = __webpack_require__(6); | ||
var _createConnect = __webpack_require__(3); | ||
var _createProvideDecorator2 = _interopRequireDefault(_createProvideDecorator); | ||
var _createConnect2 = _interopRequireDefault(_createConnect); | ||
var _createConnector = __webpack_require__(5); | ||
var _createConnector2 = _interopRequireDefault(_createConnector); | ||
var _createConnectDecorator = __webpack_require__(4); | ||
var _createConnectDecorator2 = _interopRequireDefault(_createConnectDecorator); | ||
function createAll(React) { | ||
// Wrapper components | ||
var Provider = _createProvider2['default'](React); | ||
var Connector = _createConnector2['default'](React); | ||
var connect = _createConnect2['default'](React); | ||
// Higher-order components (decorators) | ||
var provide = _createProvideDecorator2['default'](React, Provider); | ||
var connect = _createConnectDecorator2['default'](React, Connector); | ||
return { Provider: Provider, Connector: Connector, provide: provide, connect: connect }; | ||
return { Provider: Provider, connect: connect }; | ||
} | ||
@@ -162,15 +130,15 @@ | ||
/***/ }, | ||
/* 4 */ | ||
/* 3 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
'use strict'; | ||
/* WEBPACK VAR INJECTION */(function(process) {'use strict'; | ||
exports.__esModule = 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; }; | ||
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; }; })(); | ||
exports['default'] = createConnect; | ||
exports['default'] = createConnectDecorator; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
@@ -180,259 +148,249 @@ | ||
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) subClass.__proto__ = superClass; } | ||
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 _utilsGetDisplayName = __webpack_require__(2); | ||
var _utilsCreateStoreShape = __webpack_require__(1); | ||
var _utilsGetDisplayName2 = _interopRequireDefault(_utilsGetDisplayName); | ||
var _utilsCreateStoreShape2 = _interopRequireDefault(_utilsCreateStoreShape); | ||
var _utilsShallowEqualScalar = __webpack_require__(10); | ||
var _utilsShallowEqual = __webpack_require__(6); | ||
var _utilsShallowEqualScalar2 = _interopRequireDefault(_utilsShallowEqualScalar); | ||
var _utilsShallowEqual2 = _interopRequireDefault(_utilsShallowEqual); | ||
function createConnectDecorator(React, Connector) { | ||
var Component = React.Component; | ||
var _utilsIsPlainObject = __webpack_require__(5); | ||
return function connect(select) { | ||
return function (DecoratedComponent) { | ||
return (function (_Component) { | ||
function ConnectorDecorator() { | ||
_classCallCheck(this, ConnectorDecorator); | ||
var _utilsIsPlainObject2 = _interopRequireDefault(_utilsIsPlainObject); | ||
_Component.apply(this, arguments); | ||
} | ||
var _utilsWrapActionCreators = __webpack_require__(7); | ||
_inherits(ConnectorDecorator, _Component); | ||
var _utilsWrapActionCreators2 = _interopRequireDefault(_utilsWrapActionCreators); | ||
ConnectorDecorator.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps) { | ||
return !_utilsShallowEqualScalar2['default'](this.props, nextProps); | ||
}; | ||
var _invariant = __webpack_require__(8); | ||
ConnectorDecorator.prototype.render = function render() { | ||
var _this = this; | ||
var _invariant2 = _interopRequireDefault(_invariant); | ||
return React.createElement( | ||
Connector, | ||
{ select: function (state) { | ||
return select(state, _this.props); | ||
} }, | ||
function (stuff) { | ||
return React.createElement(DecoratedComponent, _extends({}, stuff, _this.props)); | ||
} | ||
); | ||
}; | ||
var defaultMapStateToProps = function defaultMapStateToProps() { | ||
return {}; | ||
}; | ||
var defaultMapDispatchToProps = function defaultMapDispatchToProps(dispatch) { | ||
return { dispatch: dispatch }; | ||
}; | ||
var defaultMergeProps = function defaultMergeProps(stateProps, dispatchProps, parentProps) { | ||
return _extends({}, parentProps, stateProps, dispatchProps); | ||
}; | ||
_createClass(ConnectorDecorator, null, [{ | ||
key: 'displayName', | ||
value: 'Connector(' + _utilsGetDisplayName2['default'](DecoratedComponent) + ')', | ||
enumerable: true | ||
}, { | ||
key: 'DecoratedComponent', | ||
value: DecoratedComponent, | ||
enumerable: true | ||
}]); | ||
return ConnectorDecorator; | ||
})(Component); | ||
}; | ||
}; | ||
function getDisplayName(Component) { | ||
return Component.displayName || Component.name || 'Component'; | ||
} | ||
module.exports = exports['default']; | ||
// Helps track hot reloading. | ||
var nextVersion = 0; | ||
/***/ }, | ||
/* 5 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
function createConnect(React) { | ||
var Component = React.Component; | ||
var PropTypes = React.PropTypes; | ||
'use strict'; | ||
var storeShape = _utilsCreateStoreShape2['default'](PropTypes); | ||
exports.__esModule = true; | ||
return function connect(mapStateToProps, mapDispatchToProps, mergeProps) { | ||
var shouldSubscribe = Boolean(mapStateToProps); | ||
var finalMapStateToProps = mapStateToProps || defaultMapStateToProps; | ||
var finalMapDispatchToProps = _utilsIsPlainObject2['default'](mapDispatchToProps) ? _utilsWrapActionCreators2['default'](mapDispatchToProps) : mapDispatchToProps || defaultMapDispatchToProps; | ||
var finalMergeProps = mergeProps || defaultMergeProps; | ||
var shouldUpdateStateProps = finalMapStateToProps.length > 1; | ||
var shouldUpdateDispatchProps = finalMapDispatchToProps.length > 1; | ||
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; }; | ||
// Helps track hot reloading. | ||
var version = nextVersion++; | ||
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; }; })(); | ||
function computeStateProps(store, props) { | ||
var state = store.getState(); | ||
var stateProps = shouldUpdateStateProps ? finalMapStateToProps(state, props) : finalMapStateToProps(state); | ||
exports['default'] = createConnector; | ||
_invariant2['default'](_utilsIsPlainObject2['default'](stateProps), '`mapStateToProps` must return an object. Instead received %s.', stateProps); | ||
return stateProps; | ||
} | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
function computeDispatchProps(store, props) { | ||
var dispatch = store.dispatch; | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } | ||
var dispatchProps = shouldUpdateDispatchProps ? finalMapDispatchToProps(dispatch, props) : finalMapDispatchToProps(dispatch); | ||
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) subClass.__proto__ = superClass; } | ||
_invariant2['default'](_utilsIsPlainObject2['default'](dispatchProps), '`mapDispatchToProps` must return an object. Instead received %s.', dispatchProps); | ||
return dispatchProps; | ||
} | ||
var _utilsCreateStoreShape = __webpack_require__(1); | ||
function _computeNextState(stateProps, dispatchProps, parentProps) { | ||
var mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps); | ||
_invariant2['default'](_utilsIsPlainObject2['default'](mergedProps), '`mergeProps` must return an object. Instead received %s.', mergedProps); | ||
return mergedProps; | ||
} | ||
var _utilsCreateStoreShape2 = _interopRequireDefault(_utilsCreateStoreShape); | ||
return function wrapWithConnect(WrappedComponent) { | ||
var Connect = (function (_Component) { | ||
_inherits(Connect, _Component); | ||
var _utilsShallowEqual = __webpack_require__(9); | ||
Connect.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState) { | ||
return !_utilsShallowEqual2['default'](this.state.props, nextState.props); | ||
}; | ||
var _utilsShallowEqual2 = _interopRequireDefault(_utilsShallowEqual); | ||
_createClass(Connect, null, [{ | ||
key: 'displayName', | ||
value: 'Connect(' + getDisplayName(WrappedComponent) + ')', | ||
enumerable: true | ||
}, { | ||
key: 'WrappedComponent', | ||
value: WrappedComponent, | ||
enumerable: true | ||
}, { | ||
key: 'contextTypes', | ||
value: { | ||
store: storeShape | ||
}, | ||
enumerable: true | ||
}, { | ||
key: 'propTypes', | ||
value: { | ||
store: storeShape | ||
}, | ||
enumerable: true | ||
}]); | ||
var _utilsIsPlainObject = __webpack_require__(8); | ||
function Connect(props, context) { | ||
_classCallCheck(this, Connect); | ||
var _utilsIsPlainObject2 = _interopRequireDefault(_utilsIsPlainObject); | ||
_Component.call(this, props, context); | ||
this.version = version; | ||
this.store = props.store || context.store; | ||
var _invariant = __webpack_require__(11); | ||
_invariant2['default'](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 _invariant2 = _interopRequireDefault(_invariant); | ||
this.stateProps = computeStateProps(this.store, props); | ||
this.dispatchProps = computeDispatchProps(this.store, props); | ||
this.state = { | ||
props: this.computeNextState() | ||
}; | ||
} | ||
function createConnector(React) { | ||
var Component = React.Component; | ||
var PropTypes = React.PropTypes; | ||
Connect.prototype.computeNextState = function computeNextState() { | ||
var props = arguments.length <= 0 || arguments[0] === undefined ? this.props : arguments[0]; | ||
var storeShape = _utilsCreateStoreShape2['default'](PropTypes); | ||
return _computeNextState(this.stateProps, this.dispatchProps, props); | ||
}; | ||
return (function (_Component) { | ||
function Connector(props, context) { | ||
_classCallCheck(this, Connector); | ||
Connect.prototype.updateStateProps = function updateStateProps() { | ||
var props = arguments.length <= 0 || arguments[0] === undefined ? this.props : arguments[0]; | ||
_Component.call(this, props, context); | ||
this.state = this.selectState(props, context); | ||
} | ||
var nextStateProps = computeStateProps(this.store, props); | ||
if (_utilsShallowEqual2['default'](nextStateProps, this.stateProps)) { | ||
return false; | ||
} | ||
_inherits(Connector, _Component); | ||
this.stateProps = nextStateProps; | ||
return true; | ||
}; | ||
Connector.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState) { | ||
return !this.isSliceEqual(this.state.slice, nextState.slice) || !_utilsShallowEqual2['default'](this.props, nextProps); | ||
}; | ||
Connect.prototype.updateDispatchProps = function updateDispatchProps() { | ||
var props = arguments.length <= 0 || arguments[0] === undefined ? this.props : arguments[0]; | ||
Connector.prototype.isSliceEqual = function isSliceEqual(slice, nextSlice) { | ||
var isRefEqual = slice === nextSlice; | ||
if (isRefEqual) { | ||
return true; | ||
} else if (typeof slice !== 'object' || typeof nextSlice !== 'object') { | ||
return isRefEqual; | ||
} | ||
return _utilsShallowEqual2['default'](slice, nextSlice); | ||
}; | ||
var nextDispatchProps = computeDispatchProps(this.store, props); | ||
if (_utilsShallowEqual2['default'](nextDispatchProps, this.dispatchProps)) { | ||
return false; | ||
} | ||
Connector.prototype.componentDidMount = function componentDidMount() { | ||
this.unsubscribe = this.context.store.subscribe(this.handleChange.bind(this)); | ||
this.handleChange(); | ||
}; | ||
this.dispatchProps = nextDispatchProps; | ||
return true; | ||
}; | ||
Connector.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { | ||
if (nextProps.select !== this.props.select) { | ||
// Force the state slice recalculation | ||
this.handleChange(nextProps); | ||
} | ||
}; | ||
Connect.prototype.updateState = function updateState() { | ||
var props = arguments.length <= 0 || arguments[0] === undefined ? this.props : arguments[0]; | ||
Connector.prototype.componentWillUnmount = function componentWillUnmount() { | ||
this.unsubscribe(); | ||
}; | ||
var nextState = this.computeNextState(props); | ||
if (!_utilsShallowEqual2['default'](nextState, this.state.props)) { | ||
this.setState({ | ||
props: nextState | ||
}); | ||
} | ||
}; | ||
Connector.prototype.handleChange = function handleChange() { | ||
var props = arguments[0] === undefined ? this.props : arguments[0]; | ||
Connect.prototype.isSubscribed = function isSubscribed() { | ||
return typeof this.unsubscribe === 'function'; | ||
}; | ||
var nextState = this.selectState(props, this.context); | ||
if (!this.isSliceEqual(this.state.slice, nextState.slice)) { | ||
this.setState(nextState); | ||
} | ||
}; | ||
Connect.prototype.trySubscribe = function trySubscribe() { | ||
if (shouldSubscribe && !this.unsubscribe) { | ||
this.unsubscribe = this.store.subscribe(this.handleChange.bind(this)); | ||
this.handleChange(); | ||
} | ||
}; | ||
Connector.prototype.selectState = function selectState(props, context) { | ||
var state = context.store.getState(); | ||
var slice = props.select(state); | ||
Connect.prototype.tryUnsubscribe = function tryUnsubscribe() { | ||
if (this.unsubscribe) { | ||
this.unsubscribe(); | ||
this.unsubscribe = null; | ||
} | ||
}; | ||
_invariant2['default'](_utilsIsPlainObject2['default'](slice), 'The return value of `select` prop must be an object. Instead received %s.', slice); | ||
Connect.prototype.componentDidMount = function componentDidMount() { | ||
this.trySubscribe(); | ||
}; | ||
return { slice: slice }; | ||
}; | ||
Connect.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { | ||
if (!_utilsShallowEqual2['default'](nextProps, this.props)) { | ||
if (shouldUpdateStateProps) { | ||
this.updateStateProps(nextProps); | ||
} | ||
Connector.prototype.render = function render() { | ||
var children = this.props.children; | ||
var slice = this.state.slice; | ||
var dispatch = this.context.store.dispatch; | ||
if (shouldUpdateDispatchProps) { | ||
this.updateDispatchProps(nextProps); | ||
} | ||
return children(_extends({ dispatch: dispatch }, slice)); | ||
}; | ||
this.updateState(nextProps); | ||
} | ||
}; | ||
_createClass(Connector, null, [{ | ||
key: 'contextTypes', | ||
value: { | ||
store: storeShape.isRequired | ||
}, | ||
enumerable: true | ||
}, { | ||
key: 'propTypes', | ||
value: { | ||
children: PropTypes.func.isRequired, | ||
select: PropTypes.func.isRequired | ||
}, | ||
enumerable: true | ||
}, { | ||
key: 'defaultProps', | ||
value: { | ||
select: function select(state) { | ||
return state; | ||
} | ||
}, | ||
enumerable: true | ||
}]); | ||
Connect.prototype.componentWillUnmount = function componentWillUnmount() { | ||
this.tryUnsubscribe(); | ||
}; | ||
return Connector; | ||
})(Component); | ||
} | ||
Connect.prototype.handleChange = function handleChange() { | ||
if (this.updateStateProps()) { | ||
this.updateState(); | ||
} | ||
}; | ||
module.exports = exports['default']; | ||
Connect.prototype.getWrappedInstance = function getWrappedInstance() { | ||
return this.refs.wrappedInstance; | ||
}; | ||
/***/ }, | ||
/* 6 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
Connect.prototype.render = function render() { | ||
return React.createElement(WrappedComponent, _extends({ ref: 'wrappedInstance' | ||
}, this.state.props)); | ||
}; | ||
'use strict'; | ||
return Connect; | ||
})(Component); | ||
exports.__esModule = true; | ||
if ( | ||
// Node-like CommonJS environments (Browserify, Webpack) | ||
typeof process !== 'undefined' && typeof process.env !== 'undefined' && ("development") !== 'production' || | ||
// React Native | ||
typeof __DEV__ !== 'undefined' && __DEV__ //eslint-disable-line no-undef | ||
) { | ||
Connect.prototype.componentWillUpdate = function componentWillUpdate() { | ||
if (this.version === version) { | ||
return; | ||
} | ||
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; }; })(); | ||
// We are hot reloading! | ||
this.version = version; | ||
exports['default'] = createProvideDecorator; | ||
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 _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) subClass.__proto__ = superClass; } | ||
var _utilsGetDisplayName = __webpack_require__(2); | ||
var _utilsGetDisplayName2 = _interopRequireDefault(_utilsGetDisplayName); | ||
function createProvideDecorator(React, Provider) { | ||
var Component = React.Component; | ||
return function provide(store) { | ||
return function (DecoratedComponent) { | ||
return (function (_Component) { | ||
function ProviderDecorator() { | ||
_classCallCheck(this, ProviderDecorator); | ||
_Component.apply(this, arguments); | ||
// Update the state and bindings. | ||
this.trySubscribe(); | ||
this.updateStateProps(); | ||
this.updateDispatchProps(); | ||
this.updateState(); | ||
}; | ||
} | ||
_inherits(ProviderDecorator, _Component); | ||
ProviderDecorator.prototype.render = function render() { | ||
var _this = this; | ||
return React.createElement( | ||
Provider, | ||
{ store: store }, | ||
function () { | ||
return React.createElement(DecoratedComponent, _this.props); | ||
} | ||
); | ||
}; | ||
_createClass(ProviderDecorator, null, [{ | ||
key: 'displayName', | ||
value: 'Provider(' + _utilsGetDisplayName2['default'](DecoratedComponent) + ')', | ||
enumerable: true | ||
}, { | ||
key: 'DecoratedComponent', | ||
value: DecoratedComponent, | ||
enumerable: true | ||
}]); | ||
return ProviderDecorator; | ||
})(Component); | ||
return Connect; | ||
}; | ||
@@ -443,5 +401,6 @@ }; | ||
module.exports = exports['default']; | ||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(9))) | ||
/***/ }, | ||
/* 7 */ | ||
/* 4 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -461,3 +420,3 @@ | ||
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) subClass.__proto__ = superClass; } | ||
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; } | ||
@@ -468,16 +427,45 @@ var _utilsCreateStoreShape = __webpack_require__(1); | ||
function isUsingOwnerContext(React) { | ||
var version = React.version; | ||
if (typeof version !== 'string') { | ||
return false; | ||
} | ||
var sections = version.split('.'); | ||
var major = parseInt(sections[0], 10); | ||
var minor = parseInt(sections[1], 10); | ||
return major === 0 && minor === 13; | ||
} | ||
function createProvider(React) { | ||
var Component = React.Component; | ||
var PropTypes = React.PropTypes; | ||
var Children = React.Children; | ||
var storeShape = _utilsCreateStoreShape2['default'](PropTypes); | ||
var requireFunctionChild = isUsingOwnerContext(React); | ||
return (function (_Component) { | ||
function Provider(props, context) { | ||
_classCallCheck(this, Provider); | ||
var didWarn = false; | ||
function warnAboutFunction() { | ||
if (didWarn || requireFunctionChild) { | ||
return; | ||
} | ||
_Component.call(this, props, context); | ||
this.state = { store: props.store }; | ||
didWarn = true; | ||
console.error( // eslint-disable-line no-console | ||
'With React 0.14 and later versions, you no longer need to ' + 'wrap <Provider> child into a function.'); | ||
} | ||
function warnAboutElement() { | ||
if (didWarn || !requireFunctionChild) { | ||
return; | ||
} | ||
didWarn = true; | ||
console.error( // eslint-disable-line no-console | ||
'With React 0.13, you need to ' + 'wrap <Provider> child into a function. ' + 'This restriction will be removed with React 0.14.'); | ||
} | ||
return (function (_Component) { | ||
_inherits(Provider, _Component); | ||
@@ -489,2 +477,24 @@ | ||
_createClass(Provider, null, [{ | ||
key: 'childContextTypes', | ||
value: { | ||
store: storeShape.isRequired | ||
}, | ||
enumerable: true | ||
}, { | ||
key: 'propTypes', | ||
value: { | ||
store: storeShape.isRequired, | ||
children: (requireFunctionChild ? PropTypes.func : PropTypes.element).isRequired | ||
}, | ||
enumerable: true | ||
}]); | ||
function Provider(props, context) { | ||
_classCallCheck(this, Provider); | ||
_Component.call(this, props, context); | ||
this.state = { store: props.store }; | ||
} | ||
Provider.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { | ||
@@ -503,19 +513,12 @@ var store = this.state.store; | ||
return children(); | ||
if (typeof children === 'function') { | ||
warnAboutFunction(); | ||
children = children(); | ||
} else { | ||
warnAboutElement(); | ||
} | ||
return Children.only(children); | ||
}; | ||
_createClass(Provider, null, [{ | ||
key: 'childContextTypes', | ||
value: { | ||
store: storeShape.isRequired | ||
}, | ||
enumerable: true | ||
}, { | ||
key: 'propTypes', | ||
value: { | ||
children: PropTypes.func.isRequired | ||
}, | ||
enumerable: true | ||
}]); | ||
return Provider; | ||
@@ -528,3 +531,3 @@ })(Component); | ||
/***/ }, | ||
/* 8 */ | ||
/* 5 */ | ||
/***/ function(module, exports) { | ||
@@ -536,5 +539,25 @@ | ||
exports['default'] = isPlainObject; | ||
var fnToString = function fnToString(fn) { | ||
return Function.prototype.toString.call(fn); | ||
}; | ||
/** | ||
* @param {any} obj The object to inspect. | ||
* @returns {boolean} True if the argument appears to be a plain object. | ||
*/ | ||
function isPlainObject(obj) { | ||
return obj ? typeof obj === 'object' && Object.getPrototypeOf(obj) === Object.prototype : false; | ||
if (!obj || typeof obj !== 'object') { | ||
return false; | ||
} | ||
var proto = typeof obj.constructor === 'function' ? Object.getPrototypeOf(obj) : Object.prototype; | ||
if (proto === null) { | ||
return true; | ||
} | ||
var constructor = proto.constructor; | ||
return typeof constructor === 'function' && constructor instanceof constructor && fnToString(constructor) === fnToString(Object); | ||
} | ||
@@ -545,3 +568,3 @@ | ||
/***/ }, | ||
/* 9 */ | ||
/* 6 */ | ||
/***/ function(module, exports) { | ||
@@ -580,4 +603,4 @@ | ||
/***/ }, | ||
/* 10 */ | ||
/***/ function(module, exports) { | ||
/* 7 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -587,36 +610,10 @@ 'use strict'; | ||
exports.__esModule = true; | ||
exports['default'] = shallowEqualScalar; | ||
exports['default'] = wrapActionCreators; | ||
function shallowEqualScalar(objA, objB) { | ||
if (objA === objB) { | ||
return true; | ||
} | ||
var _redux = __webpack_require__(11); | ||
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { | ||
return false; | ||
} | ||
var keysA = Object.keys(objA); | ||
var keysB = Object.keys(objB); | ||
if (keysA.length !== keysB.length) { | ||
return false; | ||
} | ||
// Test for A's keys different from B. | ||
var hasOwn = Object.prototype.hasOwnProperty; | ||
for (var i = 0; i < keysA.length; i++) { | ||
if (!hasOwn.call(objB, keysA[i])) { | ||
return false; | ||
} | ||
var valA = objA[keysA[i]]; | ||
var valB = objB[keysA[i]]; | ||
if (valA !== valB || typeof valA === 'object' || typeof valB === 'object') { | ||
return false; | ||
} | ||
} | ||
return true; | ||
function wrapActionCreators(actionCreators) { | ||
return function (dispatch) { | ||
return _redux.bindActionCreators(actionCreators, dispatch); | ||
}; | ||
} | ||
@@ -627,3 +624,3 @@ | ||
/***/ }, | ||
/* 11 */ | ||
/* 8 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
@@ -656,3 +653,3 @@ | ||
var invariant = function(condition, format, a, b, c, d, e, f) { | ||
if ((undefined) !== 'production') { | ||
if (true) { | ||
if (format === undefined) { | ||
@@ -688,7 +685,109 @@ throw new Error('invariant requires an error message argument'); | ||
/***/ }, | ||
/* 12 */ | ||
/* 9 */ | ||
/***/ function(module, exports) { | ||
module.exports = __WEBPACK_EXTERNAL_MODULE_12__; | ||
// shim for using process in browser | ||
var process = module.exports = {}; | ||
var queue = []; | ||
var draining = false; | ||
var currentQueue; | ||
var queueIndex = -1; | ||
function cleanUpNextTick() { | ||
draining = false; | ||
if (currentQueue.length) { | ||
queue = currentQueue.concat(queue); | ||
} else { | ||
queueIndex = -1; | ||
} | ||
if (queue.length) { | ||
drainQueue(); | ||
} | ||
} | ||
function drainQueue() { | ||
if (draining) { | ||
return; | ||
} | ||
var timeout = setTimeout(cleanUpNextTick); | ||
draining = true; | ||
var len = queue.length; | ||
while(len) { | ||
currentQueue = queue; | ||
queue = []; | ||
while (++queueIndex < len) { | ||
currentQueue[queueIndex].run(); | ||
} | ||
queueIndex = -1; | ||
len = queue.length; | ||
} | ||
currentQueue = null; | ||
draining = false; | ||
clearTimeout(timeout); | ||
} | ||
process.nextTick = function (fun) { | ||
var args = new Array(arguments.length - 1); | ||
if (arguments.length > 1) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
args[i - 1] = arguments[i]; | ||
} | ||
} | ||
queue.push(new Item(fun, args)); | ||
if (queue.length === 1 && !draining) { | ||
setTimeout(drainQueue, 0); | ||
} | ||
}; | ||
// v8 likes predictible objects | ||
function Item(fun, array) { | ||
this.fun = fun; | ||
this.array = array; | ||
} | ||
Item.prototype.run = function () { | ||
this.fun.apply(null, this.array); | ||
}; | ||
process.title = 'browser'; | ||
process.browser = true; | ||
process.env = {}; | ||
process.argv = []; | ||
process.version = ''; // empty string to avoid regexp issues | ||
process.versions = {}; | ||
function noop() {} | ||
process.on = noop; | ||
process.addListener = noop; | ||
process.once = noop; | ||
process.off = noop; | ||
process.removeListener = noop; | ||
process.removeAllListeners = noop; | ||
process.emit = noop; | ||
process.binding = function (name) { | ||
throw new Error('process.binding is not supported'); | ||
}; | ||
// TODO(shtylman) | ||
process.cwd = function () { return '/' }; | ||
process.chdir = function (dir) { | ||
throw new Error('process.chdir is not supported'); | ||
}; | ||
process.umask = function() { return 0; }; | ||
/***/ }, | ||
/* 10 */ | ||
/***/ function(module, exports) { | ||
module.exports = __WEBPACK_EXTERNAL_MODULE_10__; | ||
/***/ }, | ||
/* 11 */ | ||
/***/ function(module, exports) { | ||
module.exports = __WEBPACK_EXTERNAL_MODULE_11__; | ||
/***/ } | ||
@@ -695,0 +794,0 @@ /******/ ]) |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define(["react"],t):"object"==typeof exports?exports.ReduxReact=t(require("react")):e.ReduxReact=t(e.React)}(this,function(e){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var o=n(12),u=r(o),i=n(3),a=r(i),c=a.default(u.default),s=c.Provider,l=c.Connector,f=c.provide,p=c.connect;t.Provider=s,t.Connector=l,t.provide=f,t.connect=p},function(e,t){"use strict";function n(e){return e.shape({subscribe:e.func.isRequired,dispatch:e.func.isRequired,getState:e.func.isRequired})}t.__esModule=!0,t.default=n,e.exports=t.default},function(e,t){"use strict";function n(e){return e.displayName||e.name||"Component"}t.__esModule=!0,t.default=n,e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){var t=i.default(e),n=l.default(e),r=c.default(e,t),o=p.default(e,n);return{Provider:t,Connector:n,provide:r,connect:o}}t.__esModule=!0,t.default=o;var u=n(7),i=r(u),a=n(6),c=r(a),s=n(5),l=r(s),f=n(4),p=r(f);e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(e.__proto__=t)}function i(e,t){var n=e.Component;return function(r){return function(i){return function(n){function s(){o(this,s),n.apply(this,arguments)}return u(s,n),s.prototype.shouldComponentUpdate=function(e){return!p.default(this.props,e)},s.prototype.render=function(){var n=this;return e.createElement(t,{select:function(e){return r(e,n.props)}},function(t){return e.createElement(i,a({},t,n.props))})},c(s,null,[{key:"displayName",value:"Connector("+l.default(i)+")",enumerable:!0},{key:"DecoratedComponent",value:i,enumerable:!0}]),s}(n)}}}t.__esModule=!0;var a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},c=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();t.default=i;var s=n(2),l=r(s),f=n(10),p=r(f);e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(e.__proto__=t)}function i(e){var t=e.Component,n=e.PropTypes,r=l.default(n);return function(e){function t(n,r){o(this,t),e.call(this,n,r),this.state=this.selectState(n,r)}return u(t,e),t.prototype.shouldComponentUpdate=function(e,t){return!this.isSliceEqual(this.state.slice,t.slice)||!p.default(this.props,e)},t.prototype.isSliceEqual=function(e,t){var n=e===t;return n?!0:"object"!=typeof e||"object"!=typeof t?n:p.default(e,t)},t.prototype.componentDidMount=function(){this.unsubscribe=this.context.store.subscribe(this.handleChange.bind(this)),this.handleChange()},t.prototype.componentWillReceiveProps=function(e){e.select!==this.props.select&&this.handleChange(e)},t.prototype.componentWillUnmount=function(){this.unsubscribe()},t.prototype.handleChange=function(){var e=void 0===arguments[0]?this.props:arguments[0],t=this.selectState(e,this.context);this.isSliceEqual(this.state.slice,t.slice)||this.setState(t)},t.prototype.selectState=function(e,t){var n=t.store.getState(),r=e.select(n);return b.default(y.default(r),"The return value of `select` prop must be an object. Instead received %s.",r),{slice:r}},t.prototype.render=function(){var e=this.props.children,t=this.state.slice,n=this.context.store.dispatch;return e(a({dispatch:n},t))},c(t,null,[{key:"contextTypes",value:{store:r.isRequired},enumerable:!0},{key:"propTypes",value:{children:n.func.isRequired,select:n.func.isRequired},enumerable:!0},{key:"defaultProps",value:{select:function(e){return e}},enumerable:!0}]),t}(t)}t.__esModule=!0;var a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},c=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();t.default=i;var s=n(1),l=r(s),f=n(9),p=r(f),d=n(8),y=r(d),h=n(11),b=r(h);e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(e.__proto__=t)}function i(e,t){var n=e.Component;return function(r){return function(i){return function(n){function c(){o(this,c),n.apply(this,arguments)}return u(c,n),c.prototype.render=function(){var n=this;return e.createElement(t,{store:r},function(){return e.createElement(i,n.props)})},a(c,null,[{key:"displayName",value:"Provider("+s.default(i)+")",enumerable:!0},{key:"DecoratedComponent",value:i,enumerable:!0}]),c}(n)}}}t.__esModule=!0;var a=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();t.default=i;var c=n(2),s=r(c);e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(e.__proto__=t)}function i(e){var t=e.Component,n=e.PropTypes,r=s.default(n);return function(e){function t(n,r){o(this,t),e.call(this,n,r),this.state={store:n.store}}return u(t,e),t.prototype.getChildContext=function(){return{store:this.state.store}},t.prototype.componentWillReceiveProps=function(e){var t=this.state.store,n=e.store;if(t!==n){var r=n.getReducer();t.replaceReducer(r)}},t.prototype.render=function(){var e=this.props.children;return e()},a(t,null,[{key:"childContextTypes",value:{store:r.isRequired},enumerable:!0},{key:"propTypes",value:{children:n.func.isRequired},enumerable:!0}]),t}(t)}t.__esModule=!0;var a=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();t.default=i;var c=n(1),s=r(c);e.exports=t.default},function(e,t){"use strict";function n(e){return e?"object"==typeof e&&Object.getPrototypeOf(e)===Object.prototype:!1}t.__esModule=!0,t.default=n,e.exports=t.default},function(e,t){"use strict";function n(e,t){if(e===t)return!0;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(var o=Object.prototype.hasOwnProperty,u=0;u<n.length;u++)if(!o.call(t,n[u])||e[n[u]]!==t[n[u]])return!1;return!0}t.__esModule=!0,t.default=n,e.exports=t.default},function(e,t){"use strict";function n(e,t){if(e===t)return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(var o=Object.prototype.hasOwnProperty,u=0;u<n.length;u++){if(!o.call(t,n[u]))return!1;var i=e[n[u]],a=t[n[u]];if(i!==a||"object"==typeof i||"object"==typeof a)return!1}return!0}t.__esModule=!0,t.default=n,e.exports=t.default},function(e,t,n){"use strict";var r=function(e,t,n,r,o,u,i,a){if(!e){var c;if(void 0===t)c=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var s=[n,r,o,u,i,a],l=0;c=new Error("Invariant Violation: "+t.replace(/%s/g,function(){return s[l++]}))}throw c.framesToPop=1,c}};e.exports=r},function(t,n){t.exports=e}])}); | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react"),require("redux")):"function"==typeof define&&define.amd?define(["react","redux"],e):"object"==typeof exports?exports.ReactRedux=e(require("react"),require("redux")):t.ReactRedux=e(t.React,t.Redux)}(this,function(t,e){return function(t){function e(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return t[n].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var r={};return e.m=t,e.c=r,e.p="",e(0)}([function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}e.__esModule=!0;var o=r(10),u=n(o),i=r(2),s=n(i),a=s.default(u.default),c=a.Provider,p=a.connect;e.Provider=c,e.connect=p},function(t,e){"use strict";function r(t){return t.shape({subscribe:t.func.isRequired,dispatch:t.func.isRequired,getState:t.func.isRequired})}e.__esModule=!0,e.default=r,t.exports=e.default},function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t){var e=i.default(t),r=a.default(t);return{Provider:e,connect:r}}e.__esModule=!0,e.default=o;var u=r(4),i=n(u),s=r(3),a=n(s);t.exports=e.default},function(t,e,r){(function(n){"use strict";function o(t){return t&&t.__esModule?t:{"default":t}}function u(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function s(t){return t.displayName||t.name||"Component"}function a(t){var e=t.Component,r=t.PropTypes,o=l.default(r);return function(r,a,f){function l(t,e){var r=t.getState(),n=R?g(r,e):g(r);return _.default(v.default(n),"`mapStateToProps` must return an object. Instead received %s.",n),n}function d(t,e){var r=t.dispatch,n=T?O(r,e):O(r);return _.default(v.default(n),"`mapDispatchToProps` must return an object. Instead received %s.",n),n}function y(t,e,r){var n=S(t,e,r);return _.default(v.default(n),"`mergeProps` must return an object. Instead received %s.",n),n}var b=Boolean(r),g=r||x,O=v.default(a)?m.default(a):a||P,S=f||w,R=g.length>1,T=O.length>1,C=j++;return function(r){var a=function(e){function n(t,r){u(this,n),e.call(this,t,r),this.version=C,this.store=t.store||r.store,_.default(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+'".')),this.stateProps=l(this.store,t),this.dispatchProps=d(this.store,t),this.state={props:this.computeNextState()}}return i(n,e),n.prototype.shouldComponentUpdate=function(t,e){return!h.default(this.state.props,e.props)},c(n,null,[{key:"displayName",value:"Connect("+s(r)+")",enumerable:!0},{key:"WrappedComponent",value:r,enumerable:!0},{key:"contextTypes",value:{store:o},enumerable:!0},{key:"propTypes",value:{store:o},enumerable:!0}]),n.prototype.computeNextState=function(){var t=arguments.length<=0||void 0===arguments[0]?this.props:arguments[0];return y(this.stateProps,this.dispatchProps,t)},n.prototype.updateStateProps=function(){var t=arguments.length<=0||void 0===arguments[0]?this.props:arguments[0],e=l(this.store,t);return h.default(e,this.stateProps)?!1:(this.stateProps=e,!0)},n.prototype.updateDispatchProps=function(){var t=arguments.length<=0||void 0===arguments[0]?this.props:arguments[0],e=d(this.store,t);return h.default(e,this.dispatchProps)?!1:(this.dispatchProps=e,!0)},n.prototype.updateState=function(){var t=arguments.length<=0||void 0===arguments[0]?this.props:arguments[0],e=this.computeNextState(t);h.default(e,this.state.props)||this.setState({props:e})},n.prototype.isSubscribed=function(){return"function"==typeof this.unsubscribe},n.prototype.trySubscribe=function(){b&&!this.unsubscribe&&(this.unsubscribe=this.store.subscribe(this.handleChange.bind(this)),this.handleChange())},n.prototype.tryUnsubscribe=function(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null)},n.prototype.componentDidMount=function(){this.trySubscribe()},n.prototype.componentWillReceiveProps=function(t){h.default(t,this.props)||(R&&this.updateStateProps(t),T&&this.updateDispatchProps(t),this.updateState(t))},n.prototype.componentWillUnmount=function(){this.tryUnsubscribe()},n.prototype.handleChange=function(){this.updateStateProps()&&this.updateState()},n.prototype.getWrappedInstance=function(){return this.refs.wrappedInstance},n.prototype.render=function(){return t.createElement(r,p({ref:"wrappedInstance"},this.state.props))},n}(e);return"undefined"!=typeof n&&"undefined"!=typeof n.env,"undefined"!=typeof __DEV__&&__DEV__&&(a.prototype.componentWillUpdate=function(){this.version!==C&&(this.version=C,this.trySubscribe(),this.updateStateProps(),this.updateDispatchProps(),this.updateState())}),a}}}e.__esModule=!0;var c=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),p=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(t[n]=r[n])}return t};e.default=a;var f=r(1),l=o(f),d=r(6),h=o(d),y=r(5),v=o(y),b=r(7),m=o(b),g=r(8),_=o(g),x=function(){return{}},P=function(t){return{dispatch:t}},w=function(t,e,r){return p({},r,t,e)},j=0;t.exports=e.default}).call(e,r(9))},function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function u(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function i(t){var e=t.version;if("string"!=typeof e)return!1;var r=e.split("."),n=parseInt(r[0],10),o=parseInt(r[1],10);return 0===n&&13===o}function s(t){function e(){d||l||(d=!0,console.error("With React 0.14 and later versions, you no longer need to wrap <Provider> child into a function."))}function r(){!d&&l&&(d=!0,console.error("With React 0.13, you need to wrap <Provider> child into a function. This restriction will be removed with React 0.14."))}var n=t.Component,s=t.PropTypes,c=t.Children,f=p.default(s),l=i(t),d=!1;return function(t){function n(e,r){o(this,n),t.call(this,e,r),this.state={store:e.store}}return u(n,t),n.prototype.getChildContext=function(){return{store:this.state.store}},a(n,null,[{key:"childContextTypes",value:{store:f.isRequired},enumerable:!0},{key:"propTypes",value:{store:f.isRequired,children:(l?s.func:s.element).isRequired},enumerable:!0}]),n.prototype.componentWillReceiveProps=function(t){var e=this.state.store,r=t.store;if(e!==r){var n=r.getReducer();e.replaceReducer(n)}},n.prototype.render=function(){var t=this.props.children;return"function"==typeof t?(e(),t=t()):r(),c.only(t)},n}(n)}e.__esModule=!0;var a=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}();e.default=s;var c=r(1),p=n(c);t.exports=e.default},function(t,e){"use strict";function r(t){if(!t||"object"!=typeof t)return!1;var e="function"==typeof t.constructor?Object.getPrototypeOf(t):Object.prototype;if(null===e)return!0;var r=e.constructor;return"function"==typeof r&&r instanceof r&&n(r)===n(Object)}e.__esModule=!0,e.default=r;var n=function(t){return Function.prototype.toString.call(t)};t.exports=e.default},function(t,e){"use strict";function r(t,e){if(t===e)return!0;var r=Object.keys(t),n=Object.keys(e);if(r.length!==n.length)return!1;for(var o=Object.prototype.hasOwnProperty,u=0;u<r.length;u++)if(!o.call(e,r[u])||t[r[u]]!==e[r[u]])return!1;return!0}e.__esModule=!0,e.default=r,t.exports=e.default},function(t,e,r){"use strict";function n(t){return function(e){return o.bindActionCreators(t,e)}}e.__esModule=!0,e.default=n;var o=r(11);t.exports=e.default},function(t,e,r){"use strict";var n=function(t,e,r,n,o,u,i,s){if(!t){var a;if(void 0===e)a=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[r,n,o,u,i,s],p=0;a=new Error("Invariant Violation: "+e.replace(/%s/g,function(){return c[p++]}))}throw a.framesToPop=1,a}};t.exports=n},function(t,e){function r(){c=!1,i.length?a=i.concat(a):p=-1,a.length&&n()}function n(){if(!c){var t=setTimeout(r);c=!0;for(var e=a.length;e;){for(i=a,a=[];++p<e;)i[p].run();p=-1,e=a.length}i=null,c=!1,clearTimeout(t)}}function o(t,e){this.fun=t,this.array=e}function u(){}var i,s=t.exports={},a=[],c=!1,p=-1;s.nextTick=function(t){var e=new Array(arguments.length-1);if(arguments.length>1)for(var r=1;r<arguments.length;r++)e[r-1]=arguments[r];a.push(new o(t,e)),1!==a.length||c||setTimeout(n,0)},o.prototype.run=function(){this.fun.apply(null,this.array)},s.title="browser",s.browser=!0,s.env={},s.argv=[],s.version="",s.versions={},s.on=u,s.addListener=u,s.once=u,s.off=u,s.removeListener=u,s.removeAllListeners=u,s.emit=u,s.binding=function(t){throw new Error("process.binding is not supported")},s.cwd=function(){return"/"},s.chdir=function(t){throw new Error("process.chdir is not supported")},s.umask=function(){return 0}},function(e,r){e.exports=t},function(t,r){t.exports=e}])}); |
@@ -12,26 +12,13 @@ 'use strict'; | ||
var _createProvideDecorator = require('./createProvideDecorator'); | ||
var _createConnect = require('./createConnect'); | ||
var _createProvideDecorator2 = _interopRequireDefault(_createProvideDecorator); | ||
var _createConnect2 = _interopRequireDefault(_createConnect); | ||
var _createConnector = require('./createConnector'); | ||
var _createConnector2 = _interopRequireDefault(_createConnector); | ||
var _createConnectDecorator = require('./createConnectDecorator'); | ||
var _createConnectDecorator2 = _interopRequireDefault(_createConnectDecorator); | ||
function createAll(React) { | ||
// Wrapper components | ||
var Provider = _createProvider2['default'](React); | ||
var Connector = _createConnector2['default'](React); | ||
var connect = _createConnect2['default'](React); | ||
// Higher-order components (decorators) | ||
var provide = _createProvideDecorator2['default'](React, Provider); | ||
var connect = _createConnectDecorator2['default'](React, Connector); | ||
return { Provider: Provider, Connector: Connector, provide: provide, connect: connect }; | ||
return { Provider: Provider, connect: connect }; | ||
} | ||
module.exports = exports['default']; |
@@ -13,3 +13,3 @@ 'use strict'; | ||
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) subClass.__proto__ = superClass; } | ||
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; } | ||
@@ -20,16 +20,45 @@ var _utilsCreateStoreShape = require('../utils/createStoreShape'); | ||
function isUsingOwnerContext(React) { | ||
var version = React.version; | ||
if (typeof version !== 'string') { | ||
return false; | ||
} | ||
var sections = version.split('.'); | ||
var major = parseInt(sections[0], 10); | ||
var minor = parseInt(sections[1], 10); | ||
return major === 0 && minor === 13; | ||
} | ||
function createProvider(React) { | ||
var Component = React.Component; | ||
var PropTypes = React.PropTypes; | ||
var Children = React.Children; | ||
var storeShape = _utilsCreateStoreShape2['default'](PropTypes); | ||
var requireFunctionChild = isUsingOwnerContext(React); | ||
return (function (_Component) { | ||
function Provider(props, context) { | ||
_classCallCheck(this, Provider); | ||
var didWarn = false; | ||
function warnAboutFunction() { | ||
if (didWarn || requireFunctionChild) { | ||
return; | ||
} | ||
_Component.call(this, props, context); | ||
this.state = { store: props.store }; | ||
didWarn = true; | ||
console.error( // eslint-disable-line no-console | ||
'With React 0.14 and later versions, you no longer need to ' + 'wrap <Provider> child into a function.'); | ||
} | ||
function warnAboutElement() { | ||
if (didWarn || !requireFunctionChild) { | ||
return; | ||
} | ||
didWarn = true; | ||
console.error( // eslint-disable-line no-console | ||
'With React 0.13, you need to ' + 'wrap <Provider> child into a function. ' + 'This restriction will be removed with React 0.14.'); | ||
} | ||
return (function (_Component) { | ||
_inherits(Provider, _Component); | ||
@@ -41,2 +70,24 @@ | ||
_createClass(Provider, null, [{ | ||
key: 'childContextTypes', | ||
value: { | ||
store: storeShape.isRequired | ||
}, | ||
enumerable: true | ||
}, { | ||
key: 'propTypes', | ||
value: { | ||
store: storeShape.isRequired, | ||
children: (requireFunctionChild ? PropTypes.func : PropTypes.element).isRequired | ||
}, | ||
enumerable: true | ||
}]); | ||
function Provider(props, context) { | ||
_classCallCheck(this, Provider); | ||
_Component.call(this, props, context); | ||
this.state = { store: props.store }; | ||
} | ||
Provider.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { | ||
@@ -55,19 +106,12 @@ var store = this.state.store; | ||
return children(); | ||
if (typeof children === 'function') { | ||
warnAboutFunction(); | ||
children = children(); | ||
} else { | ||
warnAboutElement(); | ||
} | ||
return Children.only(children); | ||
}; | ||
_createClass(Provider, null, [{ | ||
key: 'childContextTypes', | ||
value: { | ||
store: storeShape.isRequired | ||
}, | ||
enumerable: true | ||
}, { | ||
key: 'propTypes', | ||
value: { | ||
children: PropTypes.func.isRequired | ||
}, | ||
enumerable: true | ||
}]); | ||
return Provider; | ||
@@ -74,0 +118,0 @@ })(Component); |
@@ -18,8 +18,4 @@ 'use strict'; | ||
var Provider = _createAll.Provider; | ||
var Connector = _createAll.Connector; | ||
var provide = _createAll.provide; | ||
var connect = _createAll.connect; | ||
exports.Provider = Provider; | ||
exports.Connector = Connector; | ||
exports.provide = provide; | ||
exports.connect = connect; |
@@ -18,8 +18,4 @@ 'use strict'; | ||
var Provider = _createAll.Provider; | ||
var Connector = _createAll.Connector; | ||
var provide = _createAll.provide; | ||
var connect = _createAll.connect; | ||
exports.Provider = Provider; | ||
exports.Connector = Connector; | ||
exports.provide = provide; | ||
exports.connect = connect; |
@@ -5,7 +5,27 @@ 'use strict'; | ||
exports['default'] = isPlainObject; | ||
var fnToString = function fnToString(fn) { | ||
return Function.prototype.toString.call(fn); | ||
}; | ||
/** | ||
* @param {any} obj The object to inspect. | ||
* @returns {boolean} True if the argument appears to be a plain object. | ||
*/ | ||
function isPlainObject(obj) { | ||
return obj ? typeof obj === 'object' && Object.getPrototypeOf(obj) === Object.prototype : false; | ||
if (!obj || typeof obj !== 'object') { | ||
return false; | ||
} | ||
var proto = typeof obj.constructor === 'function' ? Object.getPrototypeOf(obj) : Object.prototype; | ||
if (proto === null) { | ||
return true; | ||
} | ||
var constructor = proto.constructor; | ||
return typeof constructor === 'function' && constructor instanceof constructor && fnToString(constructor) === fnToString(Object); | ||
} | ||
module.exports = exports['default']; |
{ | ||
"name": "react-redux", | ||
"version": "1.0.0-alpha", | ||
"description": "Redux bindings for React", | ||
"version": "1.0.0", | ||
"description": "Official React bindings for Redux", | ||
"main": "./lib/index.js", | ||
"scripts": { | ||
"browser": "scripts/browser", | ||
"build": "scripts/build", | ||
"clean": "scripts/clean", | ||
"lint": "scripts/lint", | ||
"prepublish": "scripts/prepublish", | ||
"test": "scripts/test", | ||
"test:watch": "scripts/test-watch", | ||
"test:cov": "scripts/test-cov" | ||
"build:lib": "babel src --out-dir lib", | ||
"build:umd": "webpack src/index.js dist/react-redux.js --config webpack.config.development.js", | ||
"build:umd:min": "webpack src/index.js dist/react-redux.min.js --config webpack.config.production.js", | ||
"build": "npm run build:lib && npm run build:umd && npm run build:umd:min", | ||
"clean": "rimraf lib dist coverage", | ||
"lint": "eslint src test", | ||
"prepublish": "npm run clean && npm run build", | ||
"test": "mocha --compilers js:babel/register --recursive", | ||
"test:watch": "npm test -- --watch", | ||
"test:cov": "babel-node ./node_modules/isparta/bin/isparta cover ./node_modules/mocha/bin/_mocha -- --recursive" | ||
}, | ||
@@ -38,19 +40,20 @@ "repository": { | ||
"devDependencies": { | ||
"babel": "^5.5.8", | ||
"babel-core": "5.6.15", | ||
"babel": "^5.8.21", | ||
"babel-core": "^5.8.22", | ||
"babel-eslint": "^3.1.15", | ||
"babel-loader": "^5.1.4", | ||
"babel-loader": "^5.3.2", | ||
"eslint": "^0.23", | ||
"eslint-config-airbnb": "0.0.6", | ||
"eslint-plugin-react": "^2.3.0", | ||
"expect": "^1.6.0", | ||
"istanbul": "^0.3.15", | ||
"expect": "^1.8.0", | ||
"isparta": "^3.0.3", | ||
"istanbul": "^0.3.17", | ||
"jsdom": "~5.4.3", | ||
"mocha": "^2.2.5", | ||
"mocha-jsdom": "~0.4.0", | ||
"react": "^0.13.0", | ||
"redux": ">=1.0.0-alpha", | ||
"react": "^0.14.0-beta3", | ||
"react-addons-test-utils": "^0.14.0-beta3", | ||
"redux": "^1.0.1", | ||
"rimraf": "^2.3.4", | ||
"webpack": "^1.9.6", | ||
"webpack-dev-server": "^1.8.2" | ||
"webpack": "^1.11.0" | ||
}, | ||
@@ -61,5 +64,4 @@ "dependencies": { | ||
"peerDependencies": { | ||
"redux": ">=1.0.0-alpha", | ||
"react": "^0.13.0" | ||
"redux": "^1.0.0 || 1.0.0-rc" | ||
} | ||
} |
475
README.md
@@ -1,10 +0,473 @@ | ||
react-redux | ||
React Redux | ||
========================= | ||
Work in progress | ||
Official React bindings for [Redux](https://github.com/gaearon/redux). | ||
Performant and flexible. | ||
TODO: | ||
[![build status](https://img.shields.io/travis/rackt/react-redux/master.svg?style=flat-square)](https://travis-ci.org/rackt/react-redux) [![npm version](https://img.shields.io/npm/v/react-redux.svg?style=flat-square)](https://www.npmjs.com/package/react-redux) | ||
[![npm downloads](https://img.shields.io/npm/dm/react-redux.svg?style=flat-square)](https://www.npmjs.com/package/react-redux) | ||
[![redux channel on slack](https://img.shields.io/badge/slack-redux@reactiflux-61DAFB.svg?style=flat-square)](http://www.reactiflux.com) | ||
- dedupe modules | ||
- figure out UMD | ||
- probably some other stuff. | ||
>**Note: There is a project called `redux-react` on NPM that is [completely unrelated](https://github.com/cgarvis/redux-react/issues/1) to the official bindings. This documentation (and any other official Redux documentation) is for `react-redux`.** | ||
## Table of Contents | ||
- [Installation](#installation) | ||
- [React Native](#react-native) | ||
- [Quick Start](#quick-start) | ||
- [API](#api) | ||
- [`<Provider store>`](#provider-store) | ||
- [`connect([mapStateToProps], [mapDispatchToProps], [mergeProps])`](#connectmapstatetoprops-mapdispatchtoprops-mergeprops) | ||
- [Troubleshooting](#troubleshooting) | ||
- [License](#license) | ||
## Installation | ||
``` | ||
npm install --save react-redux | ||
``` | ||
Be careful! **“React” goes first.** | ||
There is a project called `redux-react` but it is [completely unrelated](https://github.com/cgarvis/redux-react/issues/1). | ||
React Redux requires **React 0.13 or later.** | ||
## React Native | ||
What you get from `react-redux` is for React. | ||
For React Native, import from `react-redux/native` instead. | ||
## Quick Start | ||
React bindings for Redux embrace the idea of [dividing “smart” and “dumb” components](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0). | ||
It is advisable that only top-level components of your app (such as route handlers, for example) are aware of Redux. Components below them should be “dumb” and receive all data via props. | ||
<center> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th></th> | ||
<th>Location</th> | ||
<th>Use React-Redux</th> | ||
<th>To read data, they</th> | ||
<th>To change data, they</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>“Smart” Components</td> | ||
<td>Top level, route handlers</td> | ||
<td>Yes</th> | ||
<td>Subscribe to Redux state</td> | ||
<td>Dispatch Redux actions</td> | ||
</tr> | ||
<tr> | ||
<td>“Dumb” Components</td> | ||
<td>Middle and leaf components</td> | ||
<td>No</th> | ||
<td>Read data from props</td> | ||
<td>Invoke callbacks from props</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</center> | ||
### “Dumb” component is unaware of Redux | ||
Let’s say we have a `<Counter />` “dumb” component with a number `value` prop, and an `onIncrement` function prop that it will call when user presses an “Increment” button: | ||
```js | ||
import { Component } from 'react'; | ||
export default class Counter extends Component { | ||
render() { | ||
return ( | ||
<button onClick={this.props.onIncrement}> | ||
{this.props.value} | ||
</button> | ||
); | ||
} | ||
} | ||
``` | ||
### “Smart” component is `connect()`-ed to Redux | ||
Here’s how we hook it up to the Redux Store. | ||
We will use `connect()` function provided by `react-redux` to turn a “dumb” `Counter` into a smart component. The `connect()` function lets you specify *which exact* state from the Redux store your component wants to track. This lets you subscribe on any level of granularity. | ||
##### `containers/CounterContainer.js` | ||
```js | ||
import { Component } from 'react'; | ||
import { connect } from 'react-redux'; | ||
import Counter from '../components/Counter'; | ||
import { increment } from '../actionsCreators'; | ||
// Which part of the Redux global state does our component want to receive as props? | ||
function mapStateToProps(state) { | ||
return { | ||
value: state.counter | ||
}; | ||
} | ||
// Which action creators does it want to receive by props? | ||
function mapDispatchToProps(dispatch) { | ||
return { | ||
onIncrement: () => dispatch(increment()) | ||
}; | ||
} | ||
export default connect( | ||
mapStateToProps, | ||
mapDispatchToProps | ||
)(Counter); | ||
// You can also pass an object instead of defining `mapDispatchToProps`: | ||
// export default connect(mapStateToProps, CounterActionCreators)(Counter); | ||
// Or you can pass `dispatch` down as a prop if you omit `mapDispatchToProps`: | ||
// export default connect(mapStateToProps)(Counter); | ||
// See more recipes in detailed connect() examples below. | ||
``` | ||
Whether to put `connect()` call in the same file as the “dumb” component, or separately, is up to you. | ||
Ask yourself whether you'd want to reuse this component but bind it to different data, or not. | ||
### Nesting | ||
You can have many `connect()`-ed components in your app at any depth, and you can even nest them. It is however preferable that you try to only `connect()` top-level components such as route handlers, so the data flow in your application stays predictable. | ||
### Support for Decorators | ||
You might have noticed that we used parens twice. This is called partial applications, and it lets people | ||
use ES7 decorator proposal syntax: | ||
```js | ||
// Unstable syntax! It might change or break in production. | ||
@connect(mapStateToProps) | ||
export default class CounterContainer { ... } | ||
``` | ||
Don’t forget decorators are experimental! And they desugar to function calls anyway as example above demonstrates. | ||
### Additional Flexibility | ||
This is the most basic usage, but `connect()` supports many other different patterns: just passing the vanilla `dispatch()` function down, binding multiple action creators, putting them as `actions` prop, selecting parts of state and binding action creators depending on `props`, and so on. Check out `connect()` docs below to learn more. | ||
### Injecting Redux Store | ||
Finally, how do we actually hook it up to a Redux store? We need to create the store somewhere at the root of our component hierarchy. For client apps, the root component is a good place. For server rendering, you can do this in the request handler. | ||
The trick is to wrap the whole view hierarchy into `<Provider>{() => ... }</Provider>` where `Provider` is imported from `react-redux`. One gotcha is that **the child of `Provider` must be a function**. This is to work around an issue about how context (undocumented feature we have to rely on to pass Redux data to components below) works in React 0.13. In React 0.14, you will be able to put your view hierarchy in `<Provider>` without wrapping it into a function. | ||
```js | ||
import { Component } from 'react'; | ||
import { Provider } from 'react-redux'; | ||
class App extends Component { | ||
render() { | ||
// ... | ||
} | ||
} | ||
const targetEl = document.getElementById('root'); | ||
React.render(( | ||
<Provider store={store}> | ||
{() => <App />} | ||
</Provider> | ||
), targetEl); | ||
``` | ||
## API | ||
### `<Provider store>` | ||
Makes Redux store available to the `connect()` calls in the component hierarchy below. Normally, you can’t use `connect()` without wrapping the root component in `<Provider>`. (If you *really* need to, you can manually pass `store` as a prop to every `connect()`ed component, but we only recommend to do this for stubbing `store` in unit tests, or in non-fully-React codebases. Normally, you should just use `<Provider>`.) | ||
#### Props | ||
* `store`: (*[Redux Store](http://gaearon.github.io/redux/docs/api/Store.html)*): The single Redux store in your application. | ||
* `children`: (*Function*): Unlike most React components, `<Provider>` accepts a [function as a child](#child-must-be-a-function) with your root component. This is a temporary workaround for a React 0.13 context issue, which will be fixed when React 0.14 comes out. | ||
#### Example | ||
##### Vanilla React | ||
```js | ||
React.render( | ||
<Provider store={store}> | ||
{() => <MyRootComponent />} | ||
</Provider>, | ||
rootEl | ||
); | ||
``` | ||
##### React Router 0.13 | ||
```js | ||
Router.run(routes, Router.HistoryLocation, (Handler, routerState) => { // note "routerState" here | ||
React.render( | ||
<Provider store={store}> | ||
{() => <Handler routerState={routerState} />} // note "routerState" here: important to pass it down | ||
</Provider>, | ||
document.getElementById('root') | ||
); | ||
}); | ||
``` | ||
##### React Router 1.0 | ||
```js | ||
React.render( | ||
<Provider store={store}> | ||
{() => <Router history={history}>...</Router>} | ||
</Provider>, | ||
targetEl | ||
); | ||
``` | ||
### `connect([mapStateToProps], [mapDispatchToProps], [mergeProps])` | ||
Connects a React component to a Redux store. | ||
#### Arguments | ||
* [`mapStateToProps(state, [ownProps]): stateProps`] \(*Function*): If specified, the component will subscribe to Redux store updates. Any time it updates, `mapStateToProps` will be called. Its result must be a plain object, and it will be merged into the component’s props. If you omit it, the component will not be subscribed to the Redux store. If `ownProps` is specified as a second argument then `mapStateToProps` will be re-invoked whenever the component receives new props. | ||
* [`mapDispatchToProps(dispatch, [ownProps]): dispatchProps`] \(*Object* or *Function*): If an object is passed, each function inside it will be assumed to be a Redux action creator. An object with the same function names, but bound to a Redux store, will be merged into the component’s props. If a function is passed, it will be given `dispatch`. It’s up to you to return an object that somehow uses `dispatch` to bind action creators in your own way. (Tip: you may use [`bindActionCreators()`](http://gaearon.github.io/redux/docs/api/bindActionCreators.html) helper from Redux.) If you omit it, the default implementation just injects `dispatch` into your component’s props. If `ownProps` is specified as a second argument then `mapDispatchToProps` will be re-invoked whenever the component receives new props. | ||
* [`mergeProps(stateProps, dispatchProps, ownProps): props`] \(*Function*): If specified, it is passed the result of `mapStateToProps()`, `mapDispatchToProps()`, and the parent `props`. The plain object you return from it will be passed as props to the wrapped component. You may specify this function to select a slice of the state based on props, or to bind action creators to a particular variable from props. If you omit it, `Object.assign({}, ownProps, stateProps, dispatchProps)` is used by default. | ||
#### Returns | ||
A React component class that injects state and action creators into your component according to the specified options. | ||
#### Remarks | ||
* It needs to be invoked two times. First time with its arguments described above, and second time, with the component: `connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)`. | ||
* The `mapStateToProps` function takes a single argument of the entire Redux store’s state and returns an object to be passed as props. It is often called a **selector**. Use [reselect](https://github.com/faassen/reselect) to efficiently compose selectors and [compute derived data](http://gaearon.github.io/redux/docs/recipes/ComputingDerivedData.html). | ||
* **To use `connect()`, the root component of your app must be wrapped into `<Provider>{() => ... }</Provider>` before being rendered.** You may also pass `store` as a prop to the `connect()`ed component, but it's not recommended because it's just too much trouble. Only do this for in non-fully-React codebases or to stub store in a unit test. | ||
#### Examples | ||
##### Inject just `dispatch` and don't listen to store | ||
```js | ||
export default connect()(TodoApp); | ||
``` | ||
##### Inject `dispatch` and every field in the global state | ||
>Don’t do this! It kills any performance optimisations because `TodoApp` will rerender after any action. | ||
>It’s better to have more granular `connect()` on several components in your view hierarchy. | ||
```js | ||
export default connect(state => state)(TodoApp); | ||
``` | ||
##### Inject `dispatch` and `todos` | ||
```js | ||
function mapStateToProps(state) { | ||
return { todos: state.todos }; | ||
} | ||
export default connect(mapStateToProps)(TodoApp); | ||
``` | ||
##### Inject `todos` and all action creators (`addTodo`, `completeTodo`, ...) | ||
```js | ||
import * as actionCreators from './actionCreators'; | ||
function mapStateToProps(state) { | ||
return { todos: state.todos }; | ||
} | ||
export default connect(mapStateToProps, actionCreators)(TodoApp); | ||
``` | ||
##### Inject `todos` and all action creators (`addTodo`, `completeTodo`, ...) as `actions` | ||
```js | ||
import * as actionCreators from './actionCreators'; | ||
import { bindActionCreators } from 'redux'; | ||
function mapStateToProps(state) { | ||
return { todos: state.todos }; | ||
} | ||
function mapDispatchToProps(dispatch) { | ||
return { actions: bindActionCreators(actionCreators, dispatch) }; | ||
} | ||
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp); | ||
``` | ||
##### Inject `todos` and a specific action creator (`addTodo`) | ||
```js | ||
import { addTodo } from './actionCreators'; | ||
import { bindActionCreators } from 'redux'; | ||
function mapStateToProps(state) { | ||
return { todos: state.todos }; | ||
} | ||
function mapDispatchToProps(dispatch) { | ||
return bindActionCreators({ addTodo }, dispatch); | ||
} | ||
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp); | ||
``` | ||
##### Inject `todos`, todoActionCreators as `todoActions`, and counterActionCreators as `counterActions` | ||
```js | ||
import * as todoActionCreators from './todoActionCreators'; | ||
import * as counterActionCreators from './counterActionCreators'; | ||
import { bindActionCreators } from 'redux'; | ||
function mapStateToProps(state) { | ||
return { todos: state.todos }; | ||
} | ||
function mapDispatchToProps(dispatch) { | ||
return { | ||
todoActions: bindActionCreators(todoActionCreators, dispatch), | ||
counterActions: bindActionCreators(counterActionCreators, dispatch) | ||
}; | ||
} | ||
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp); | ||
``` | ||
##### Inject `todos`, and todoActionCreators and counterActionCreators together as `actions` | ||
```js | ||
import * as todoActionCreators from './todoActionCreators'; | ||
import * as counterActionCreators from './counterActionCreators'; | ||
import { bindActionCreators } from 'redux'; | ||
function mapStateToProps(state) { | ||
return { todos: state.todos }; | ||
} | ||
function mapDispatchToProps(dispatch) { | ||
return { | ||
actions: bindActionCreators(Object.assign({}, todoActionCreators, counterActionCreators), dispatch) | ||
}; | ||
} | ||
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp); | ||
``` | ||
##### Inject `todos`, and all todoActionCreators and counterActionCreators directly as props | ||
```js | ||
import * as todoActionCreators from './todoActionCreators'; | ||
import * as counterActionCreators from './counterActionCreators'; | ||
import { bindActionCreators } from 'redux'; | ||
function mapStateToProps(state) { | ||
return { todos: state.todos }; | ||
} | ||
function mapDispatchToProps(dispatch) { | ||
return bindActionCreators(Object.assign({}, todoActionCreators, counterActionCreators), dispatch); | ||
} | ||
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp); | ||
``` | ||
##### Inject `todos` of a specific user depending on props | ||
```js | ||
import * as actionCreators from './actionCreators'; | ||
function mapStateToProps(state, ownProps) { | ||
return { todos: state.todos[ownProps.userId] }; | ||
} | ||
export default connect(mapStateToProps)(TodoApp); | ||
``` | ||
##### Inject `todos` of a specific user depending on props, and inject `props.userId` into the action | ||
```js | ||
import * as actionCreators from './actionCreators'; | ||
function mapStateToProps(state) { | ||
return { todos: state.todos }; | ||
} | ||
function mergeProps(stateProps, dispatchProps, ownProps) { | ||
return Object.assign({}, ownProps, { | ||
todos: stateProps.todos[ownProps.userId], | ||
addTodo: (text) => dispatchProps.addTodo(ownProps.userId, text) | ||
}); | ||
} | ||
export default connect(mapStateToProps, actionCreators, mergeProps)(TodoApp); | ||
``` | ||
## Troubleshooting | ||
Make sure to check out [Troubleshooting Redux](http://gaearon.github.io/redux/docs/Troubleshooting.html) first. | ||
### My views aren’t updating! | ||
See the link above. | ||
In short, | ||
* Reducers should never mutate state, they must return new objects, or React Redux won’t see the updates. | ||
* Make sure you either bind action creators with `mapDispatchToState` argument to `connect()` or with `bindActionCreators()` method, or that you manually call `dispatch()`. Just calling your `MyActionCreators.addTodo()` function won’t work because it just *returns* an action, but not *dispatches* it. | ||
### My views aren’t updating on route change with React Router 0.13 | ||
If you’re using React Router 0.13, you might [bump into this problem](https://github.com/gaearon/react-redux/issues/43). The solution is simple: whenever you use `<RouteHandler>` or the `Handler` provided by `Router.run`, pass the router state to it. | ||
Root view: | ||
```js | ||
Router.run(routes, Router.HistoryLocation, (Handler, routerState) => { // note "routerState" here | ||
React.render( | ||
<Provider store={store}> | ||
{() => <Handler routerState={routerState} />} // note "routerState" here | ||
</Provider>, | ||
document.getElementById('root') | ||
); | ||
}); | ||
``` | ||
Nested view: | ||
```js | ||
render() { | ||
// Keep passing it down | ||
return <RouteHandler routerState={this.props.routerState} />; | ||
} | ||
``` | ||
Conveniently, this gives your components access to the router state! | ||
You can also upgrade to React Router 1.0 which shouldn’t have this problem. (Let us know if it does!) | ||
### Could not find "store" in either the context or props | ||
If you have context issues, | ||
1. [Make sure you don’t have duplicate React](https://medium.com/@dan_abramov/two-weird-tricks-that-fix-react-7cf9bbdef375) on the page. | ||
2. Make sure you didn’t forget to wrap your root component in [`<Provider>`](#provider-store). | ||
3. If you use React Router, something like `<Provider>{() => routes}</Provider>` won’t work. Due to the way context works in React 0.13, it’s important that the `<Provider>` children are *created* inside that function. Just referencing an outside variable doesn’t do the trick. Instead of `<Provider>{() => routes}</Provider>`, write `<Provider>{createRoutes}</Provider>` where `createRoutes()` is a function that actually *creates* (and returns) the route configuration. | ||
## License | ||
MIT |
import expect from 'expect'; | ||
import jsdomReact from './jsdomReact'; | ||
import React, { PropTypes, Component } from 'react/addons'; | ||
import jsdom from 'mocha-jsdom'; | ||
import React, { createClass, Children, PropTypes, Component } from 'react'; | ||
import TestUtils from 'react-addons-test-utils'; | ||
import { createStore } from 'redux'; | ||
import { connect, Connector } from '../../src/index'; | ||
import { connect } from '../../src/index'; | ||
const { TestUtils } = React.addons; | ||
describe('React', () => { | ||
describe('connect', () => { | ||
jsdomReact(); | ||
jsdom(); | ||
// Mock minimal Provider interface | ||
class Provider extends Component { | ||
class Passthrough extends Component { | ||
render() { | ||
return <div {...this.props} />; | ||
} | ||
} | ||
class ProviderMock extends Component { | ||
static childContextTypes = { | ||
@@ -24,15 +28,43 @@ store: PropTypes.object.isRequired | ||
render() { | ||
return this.props.children(); | ||
return Children.only(this.props.children); | ||
} | ||
} | ||
it('should wrap the component into Provider', () => { | ||
function stringBuilder(prev = '', action) { | ||
return action.type === 'APPEND' | ||
? prev + action.body | ||
: prev; | ||
} | ||
it('should receive the store in the context', () => { | ||
const store = createStore(() => ({})); | ||
@connect() | ||
class Container extends Component { | ||
render() { | ||
return <div {...this.props} />; | ||
} | ||
} | ||
const tree = TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<Container pass="through" /> | ||
</ProviderMock> | ||
); | ||
const container = TestUtils.findRenderedComponentWithType(tree, Container); | ||
expect(container.context.store).toBe(store); | ||
}); | ||
it('should pass state and props to the given component', () => { | ||
const store = createStore(() => ({ | ||
foo: 'bar' | ||
foo: 'bar', | ||
baz: 42, | ||
hello: 'world' | ||
})); | ||
@connect(state => state) | ||
@connect(({ foo, baz }) => ({ foo, baz })) | ||
class Container extends Component { | ||
render() { | ||
return <div {...this.props} />; | ||
return <Passthrough {...this.props} />; | ||
} | ||
@@ -42,14 +74,64 @@ } | ||
const container = TestUtils.renderIntoDocument( | ||
<Provider store={store}> | ||
{() => <Container pass='through' />} | ||
</Provider> | ||
<ProviderMock store={store}> | ||
<Container pass='through' baz={50} /> | ||
</ProviderMock> | ||
); | ||
const div = TestUtils.findRenderedDOMComponentWithTag(container, 'div'); | ||
expect(div.props.pass).toEqual('through'); | ||
expect(div.props.foo).toEqual('bar'); | ||
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough); | ||
expect(stub.props.pass).toEqual('through'); | ||
expect(stub.props.foo).toEqual('bar'); | ||
expect(stub.props.baz).toEqual(42); | ||
expect(stub.props.hello).toEqual(undefined); | ||
expect(() => | ||
TestUtils.findRenderedComponentWithType(container, Connector) | ||
TestUtils.findRenderedComponentWithType(container, Container) | ||
).toNotThrow(); | ||
}); | ||
it('should subscribe to the store changes', () => { | ||
const store = createStore(stringBuilder); | ||
@connect(state => ({ string: state }) ) | ||
class Container extends Component { | ||
render() { | ||
return <Passthrough {...this.props}/>; | ||
} | ||
} | ||
const tree = TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<Container /> | ||
</ProviderMock> | ||
); | ||
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough); | ||
expect(stub.props.string).toBe(''); | ||
store.dispatch({ type: 'APPEND', body: 'a'}); | ||
expect(stub.props.string).toBe('a'); | ||
store.dispatch({ type: 'APPEND', body: 'b'}); | ||
expect(stub.props.string).toBe('ab'); | ||
}); | ||
it('should handle dispatches before componentDidMount', () => { | ||
const store = createStore(stringBuilder); | ||
@connect(state => ({ string: state }) ) | ||
class Container extends Component { | ||
componentWillMount() { | ||
store.dispatch({ type: 'APPEND', body: 'a'}); | ||
} | ||
render() { | ||
return <Passthrough {...this.props}/>; | ||
} | ||
} | ||
const tree = TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<Container /> | ||
</ProviderMock> | ||
); | ||
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough); | ||
expect(stub.props.string).toBe('a'); | ||
}); | ||
it('should handle additional prop changes in addition to slice', () => { | ||
@@ -64,3 +146,3 @@ const store = createStore(() => ({ | ||
return ( | ||
<div {...this.props} pass={this.props.bar.baz} /> | ||
<Passthrough {...this.props} pass={this.props.bar.baz} /> | ||
); | ||
@@ -79,4 +161,135 @@ } | ||
} | ||
componentDidMount() { | ||
this.setState({ | ||
bar: Object.assign({}, this.state.bar, { baz: 'through' }) | ||
}); | ||
} | ||
render() { | ||
return ( | ||
<ProviderMock store={store}> | ||
<ConnectContainer bar={this.state.bar} /> | ||
</ProviderMock> | ||
); | ||
} | ||
} | ||
const container = TestUtils.renderIntoDocument(<Container />); | ||
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough); | ||
expect(stub.props.foo).toEqual('bar'); | ||
expect(stub.props.pass).toEqual('through'); | ||
}); | ||
it('should remove undefined props', () => { | ||
const store = createStore(() => ({})); | ||
let props = { x: true }; | ||
let container; | ||
@connect(() => ({}), () => ({})) | ||
class ConnectContainer extends Component { | ||
render() { | ||
return ( | ||
<Passthrough {...this.props} /> | ||
); | ||
} | ||
} | ||
class HolderContainer extends Component { | ||
render() { | ||
return ( | ||
<ConnectContainer {...props} /> | ||
); | ||
} | ||
} | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<HolderContainer ref={instance => container = instance} /> | ||
</ProviderMock> | ||
); | ||
const propsBefore = { | ||
...TestUtils.findRenderedComponentWithType(container, Passthrough).props | ||
}; | ||
props = {}; | ||
container.forceUpdate(); | ||
const propsAfter = { | ||
...TestUtils.findRenderedComponentWithType(container, Passthrough).props | ||
}; | ||
expect(propsBefore.x).toEqual(true); | ||
expect('x' in propsAfter).toEqual(false, 'x prop must be removed'); | ||
}); | ||
it('should remove undefined props without mapDispatch', () => { | ||
const store = createStore(() => ({})); | ||
let props = { x: true }; | ||
let container; | ||
@connect(() => ({})) | ||
class ConnectContainer extends Component { | ||
render() { | ||
return ( | ||
<Passthrough {...this.props} /> | ||
); | ||
} | ||
} | ||
class HolderContainer extends Component { | ||
render() { | ||
return ( | ||
<ConnectContainer {...props} /> | ||
); | ||
} | ||
} | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<HolderContainer ref={instance => container = instance} /> | ||
</ProviderMock> | ||
); | ||
const propsBefore = { | ||
...TestUtils.findRenderedComponentWithType(container, Passthrough).props | ||
}; | ||
props = {}; | ||
container.forceUpdate(); | ||
const propsAfter = { | ||
...TestUtils.findRenderedComponentWithType(container, Passthrough).props | ||
}; | ||
expect(propsBefore.x).toEqual(true); | ||
expect('x' in propsAfter).toEqual(false, 'x prop must be removed'); | ||
}); | ||
it('should ignore deep mutations in props', () => { | ||
const store = createStore(() => ({ | ||
foo: 'bar' | ||
})); | ||
@connect(state => state) | ||
class ConnectContainer extends Component { | ||
render() { | ||
return ( | ||
<Passthrough {...this.props} pass={this.props.bar.baz} /> | ||
); | ||
} | ||
} | ||
class Container extends Component { | ||
constructor() { | ||
super(); | ||
this.state = { | ||
bar: { | ||
baz: '' | ||
} | ||
}; | ||
} | ||
componentDidMount() { | ||
// Simulate deep object mutation | ||
@@ -88,7 +301,8 @@ this.state.bar.baz = 'through'; | ||
} | ||
render() { | ||
return ( | ||
<Provider store={store}> | ||
{() => <ConnectContainer bar={this.state.bar} />} | ||
</Provider> | ||
<ProviderMock store={store}> | ||
<ConnectContainer bar={this.state.bar} /> | ||
</ProviderMock> | ||
); | ||
@@ -99,62 +313,762 @@ } | ||
const container = TestUtils.renderIntoDocument(<Container />); | ||
const div = TestUtils.findRenderedDOMComponentWithTag(container, 'div'); | ||
expect(div.props.foo).toEqual('bar'); | ||
expect(div.props.pass).toEqual('through'); | ||
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough); | ||
expect(stub.props.foo).toEqual('bar'); | ||
expect(stub.props.pass).toEqual(''); | ||
}); | ||
it('should pass the only argument as the select prop down', () => { | ||
const store = createStore(() => ({ | ||
foo: 'baz', | ||
bar: 'baz' | ||
})); | ||
it('should allow for merge to incorporate state and prop changes', () => { | ||
const store = createStore(stringBuilder); | ||
function select({ foo }) { | ||
return { foo }; | ||
function doSomething(thing) { | ||
return { | ||
type: 'APPEND', | ||
body: thing | ||
}; | ||
} | ||
@connect(select) | ||
@connect( | ||
state => ({stateThing: state}), | ||
dispatch => ({ | ||
doSomething: (whatever) => dispatch(doSomething(whatever)) | ||
}), | ||
(stateProps, actionProps, parentProps) => ({ | ||
...stateProps, | ||
...actionProps, | ||
mergedDoSomething(thing) { | ||
const seed = stateProps.stateThing === '' ? 'HELLO ' : ''; | ||
actionProps.doSomething(seed + thing + parentProps.extra); | ||
} | ||
}) | ||
) | ||
class Container extends Component { | ||
render() { | ||
return <div {...this.props} />; | ||
return <Passthrough {...this.props}/>; | ||
}; | ||
} | ||
class OuterContainer extends Component { | ||
constructor() { | ||
super(); | ||
this.state = { extra: 'z' }; | ||
} | ||
render() { | ||
return ( | ||
<ProviderMock store={store}> | ||
<Container extra={this.state.extra} /> | ||
</ProviderMock> | ||
); | ||
} | ||
} | ||
const tree = TestUtils.renderIntoDocument(<OuterContainer />); | ||
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough); | ||
expect(stub.props.stateThing).toBe(''); | ||
stub.props.mergedDoSomething('a'); | ||
expect(stub.props.stateThing).toBe('HELLO az'); | ||
stub.props.mergedDoSomething('b'); | ||
expect(stub.props.stateThing).toBe('HELLO azbz'); | ||
tree.setState({extra: 'Z'}); | ||
stub.props.mergedDoSomething('c'); | ||
expect(stub.props.stateThing).toBe('HELLO azbzcZ'); | ||
}); | ||
it('should merge actionProps into WrappedComponent', () => { | ||
const store = createStore(() => ({ | ||
foo: 'bar' | ||
})); | ||
@connect( | ||
state => state, | ||
dispatch => ({ dispatch }) | ||
) | ||
class Container extends Component { | ||
render() { | ||
return <Passthrough {...this.props} />; | ||
} | ||
} | ||
const container = TestUtils.renderIntoDocument( | ||
<Provider store={store}> | ||
{() => <Container pass='through' />} | ||
</Provider> | ||
<ProviderMock store={store}> | ||
<Container pass='through' /> | ||
</ProviderMock> | ||
); | ||
const connector = TestUtils.findRenderedComponentWithType(container, Connector); | ||
expect(connector.props.select({ | ||
foo: 5, | ||
bar: 7 | ||
})).toEqual({ | ||
foo: 5 | ||
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough); | ||
expect(stub.props.dispatch).toEqual(store.dispatch); | ||
expect(stub.props.foo).toEqual('bar'); | ||
expect(() => | ||
TestUtils.findRenderedComponentWithType(container, Container) | ||
).toNotThrow(); | ||
const decorated = TestUtils.findRenderedComponentWithType(container, Container); | ||
expect(decorated.isSubscribed()).toBe(true); | ||
}); | ||
it('should not invoke mapState when props change if it only has one argument', () => { | ||
const store = createStore(stringBuilder); | ||
let invocationCount = 0; | ||
@connect(() => { | ||
invocationCount++; | ||
return {}; | ||
}) | ||
class WithoutProps extends Component { | ||
render() { | ||
return <Passthrough {...this.props}/>; | ||
} | ||
} | ||
class OuterComponent extends Component { | ||
constructor() { | ||
super(); | ||
this.state = { foo: 'FOO' }; | ||
} | ||
setFoo(foo) { | ||
this.setState({ foo }); | ||
} | ||
render() { | ||
return ( | ||
<div> | ||
<WithoutProps {...this.state} /> | ||
</div> | ||
); | ||
} | ||
} | ||
let outerComponent; | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<OuterComponent ref={c => outerComponent = c} /> | ||
</ProviderMock> | ||
); | ||
outerComponent.setFoo('BAR'); | ||
outerComponent.setFoo('DID'); | ||
expect(invocationCount).toEqual(2); | ||
}); | ||
it('should invoke mapState every time props are changed if it has a second argument', () => { | ||
const store = createStore(stringBuilder); | ||
let propsPassedIn; | ||
let invocationCount = 0; | ||
@connect((state, props) => { | ||
invocationCount++; | ||
propsPassedIn = props; | ||
return {}; | ||
}) | ||
class WithProps extends Component { | ||
render() { | ||
return <Passthrough {...this.props}/>; | ||
} | ||
} | ||
class OuterComponent extends Component { | ||
constructor() { | ||
super(); | ||
this.state = { foo: 'FOO' }; | ||
} | ||
setFoo(foo) { | ||
this.setState({ foo }); | ||
} | ||
render() { | ||
return ( | ||
<div> | ||
<WithProps {...this.state} /> | ||
</div> | ||
); | ||
} | ||
} | ||
let outerComponent; | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<OuterComponent ref={c => outerComponent = c} /> | ||
</ProviderMock> | ||
); | ||
outerComponent.setFoo('BAR'); | ||
outerComponent.setFoo('BAZ'); | ||
expect(invocationCount).toEqual(4); | ||
expect(propsPassedIn).toEqual({ | ||
foo: 'BAZ' | ||
}); | ||
}); | ||
it('should set the displayName correctly', () => { | ||
@connect(state => state) | ||
it('should not invoke mapDispatch when props change if it only has one argument', () => { | ||
const store = createStore(stringBuilder); | ||
let invocationCount = 0; | ||
@connect(null, () => { | ||
invocationCount++; | ||
return {}; | ||
}) | ||
class WithoutProps extends Component { | ||
render() { | ||
return <Passthrough {...this.props}/>; | ||
} | ||
} | ||
class OuterComponent extends Component { | ||
constructor() { | ||
super(); | ||
this.state = { foo: 'FOO' }; | ||
} | ||
setFoo(foo) { | ||
this.setState({ foo }); | ||
} | ||
render() { | ||
return ( | ||
<div> | ||
<WithoutProps {...this.state} /> | ||
</div> | ||
); | ||
} | ||
} | ||
let outerComponent; | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<OuterComponent ref={c => outerComponent = c} /> | ||
</ProviderMock> | ||
); | ||
outerComponent.setFoo('BAR'); | ||
outerComponent.setFoo('DID'); | ||
expect(invocationCount).toEqual(1); | ||
}); | ||
it('should invoke mapDispatch every time props are changed if it has a second argument', () => { | ||
const store = createStore(stringBuilder); | ||
let propsPassedIn; | ||
let invocationCount = 0; | ||
@connect(null, (dispatch, props) => { | ||
invocationCount++; | ||
propsPassedIn = props; | ||
return {}; | ||
}) | ||
class WithProps extends Component { | ||
render() { | ||
return <Passthrough {...this.props}/>; | ||
} | ||
} | ||
class OuterComponent extends Component { | ||
constructor() { | ||
super(); | ||
this.state = { foo: 'FOO' }; | ||
} | ||
setFoo(foo) { | ||
this.setState({ foo }); | ||
} | ||
render() { | ||
return ( | ||
<div> | ||
<WithProps {...this.state} /> | ||
</div> | ||
); | ||
} | ||
} | ||
let outerComponent; | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<OuterComponent ref={c => outerComponent = c} /> | ||
</ProviderMock> | ||
); | ||
outerComponent.setFoo('BAR'); | ||
outerComponent.setFoo('BAZ'); | ||
expect(invocationCount).toEqual(3); | ||
expect(propsPassedIn).toEqual({ | ||
foo: 'BAZ' | ||
}); | ||
}); | ||
it('should pass dispatch and avoid subscription if arguments are falsy', () => { | ||
const store = createStore(() => ({ | ||
foo: 'bar' | ||
})); | ||
function runCheck(...connectArgs) { | ||
@connect(...connectArgs) | ||
class Container extends Component { | ||
render() { | ||
return <Passthrough {...this.props} />; | ||
} | ||
} | ||
const container = TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<Container pass='through' /> | ||
</ProviderMock> | ||
); | ||
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough); | ||
expect(stub.props.dispatch).toEqual(store.dispatch); | ||
expect(stub.props.foo).toBe(undefined); | ||
expect(stub.props.pass).toEqual('through'); | ||
expect(() => | ||
TestUtils.findRenderedComponentWithType(container, Container) | ||
).toNotThrow(); | ||
const decorated = TestUtils.findRenderedComponentWithType(container, Container); | ||
expect(decorated.isSubscribed()).toBe(false); | ||
} | ||
runCheck(); | ||
runCheck(null, null, null); | ||
runCheck(false, false, false); | ||
}); | ||
it('should unsubscribe before unmounting', () => { | ||
const store = createStore(stringBuilder); | ||
const subscribe = store.subscribe; | ||
// Keep track of unsubscribe by wrapping subscribe() | ||
const spy = expect.createSpy(() => ({})); | ||
store.subscribe = (listener) => { | ||
const unsubscribe = subscribe(listener); | ||
return () => { | ||
spy(); | ||
return unsubscribe(); | ||
}; | ||
}; | ||
@connect( | ||
state => ({string: state}), | ||
dispatch => ({ dispatch }) | ||
) | ||
class Container extends Component { | ||
render() { | ||
return <div />; | ||
return <Passthrough {...this.props} />; | ||
} | ||
} | ||
expect(Container.displayName).toBe('Connector(Container)'); | ||
const tree = TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<Container /> | ||
</ProviderMock> | ||
); | ||
const connector = TestUtils.findRenderedComponentWithType(tree, Container); | ||
expect(spy.calls.length).toBe(0); | ||
connector.componentWillUnmount(); | ||
expect(spy.calls.length).toBe(1); | ||
}); | ||
it('should expose the wrapped component as DecoratedComponent', () => { | ||
it('should shallowly compare the selected state to prevent unnecessary updates', () => { | ||
const store = createStore(stringBuilder); | ||
const spy = expect.createSpy(() => ({})); | ||
function render({ string }) { | ||
spy(); | ||
return <Passthrough string={string}/>; | ||
} | ||
@connect( | ||
state => ({ string: state }), | ||
dispatch => ({ dispatch }) | ||
) | ||
class Container extends Component { | ||
render() { | ||
return <div />; | ||
return render(this.props); | ||
} | ||
} | ||
const tree = TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<Container /> | ||
</ProviderMock> | ||
); | ||
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough); | ||
expect(spy.calls.length).toBe(1); | ||
expect(stub.props.string).toBe(''); | ||
store.dispatch({ type: 'APPEND', body: 'a'}); | ||
expect(spy.calls.length).toBe(2); | ||
store.dispatch({ type: 'APPEND', body: 'b'}); | ||
expect(spy.calls.length).toBe(3); | ||
store.dispatch({ type: 'APPEND', body: ''}); | ||
expect(spy.calls.length).toBe(3); | ||
}); | ||
it('should shallowly compare the merged state to prevent unnecessary updates', () => { | ||
const store = createStore(stringBuilder); | ||
const spy = expect.createSpy(() => ({})); | ||
function render({ string, pass }) { | ||
spy(); | ||
return <Passthrough string={string} pass={pass} passVal={pass.val} />; | ||
} | ||
@connect( | ||
state => ({ string: state }), | ||
dispatch => ({ dispatch }), | ||
(stateProps, dispatchProps, parentProps) => ({ | ||
...dispatchProps, | ||
...stateProps, | ||
...parentProps | ||
}) | ||
) | ||
class Container extends Component { | ||
render() { | ||
return render(this.props); | ||
} | ||
} | ||
class Root extends Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { pass: '' }; | ||
} | ||
render() { | ||
return ( | ||
<ProviderMock store={store}> | ||
<Container pass={this.state.pass} /> | ||
</ProviderMock> | ||
); | ||
} | ||
} | ||
const tree = TestUtils.renderIntoDocument(<Root />); | ||
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough); | ||
expect(spy.calls.length).toBe(1); | ||
expect(stub.props.string).toBe(''); | ||
expect(stub.props.pass).toBe(''); | ||
store.dispatch({ type: 'APPEND', body: 'a'}); | ||
expect(spy.calls.length).toBe(2); | ||
expect(stub.props.string).toBe('a'); | ||
expect(stub.props.pass).toBe(''); | ||
tree.setState({ pass: '' }); | ||
expect(spy.calls.length).toBe(2); | ||
expect(stub.props.string).toBe('a'); | ||
expect(stub.props.pass).toBe(''); | ||
tree.setState({ pass: 'through' }); | ||
expect(spy.calls.length).toBe(3); | ||
expect(stub.props.string).toBe('a'); | ||
expect(stub.props.pass).toBe('through'); | ||
tree.setState({ pass: 'through' }); | ||
expect(spy.calls.length).toBe(3); | ||
expect(stub.props.string).toBe('a'); | ||
expect(stub.props.pass).toBe('through'); | ||
const obj = { prop: 'val' }; | ||
tree.setState({ pass: obj }); | ||
expect(spy.calls.length).toBe(4); | ||
expect(stub.props.string).toBe('a'); | ||
expect(stub.props.pass).toBe(obj); | ||
tree.setState({ pass: obj }); | ||
expect(spy.calls.length).toBe(4); | ||
expect(stub.props.string).toBe('a'); | ||
expect(stub.props.pass).toBe(obj); | ||
const obj2 = Object.assign({}, obj, { val: 'otherval' }); | ||
tree.setState({ pass: obj2 }); | ||
expect(spy.calls.length).toBe(5); | ||
expect(stub.props.string).toBe('a'); | ||
expect(stub.props.pass).toBe(obj2); | ||
obj2.val = 'mutation'; | ||
tree.setState({ pass: obj2 }); | ||
expect(spy.calls.length).toBe(5); | ||
expect(stub.props.string).toBe('a'); | ||
expect(stub.props.passVal).toBe('otherval'); | ||
}); | ||
it('should throw an error if mapState, mapDispatch, or mergeProps returns anything but a plain object', () => { | ||
const store = createStore(() => ({})); | ||
function makeContainer(mapState, mapDispatch, mergeProps) { | ||
return React.createElement( | ||
@connect(mapState, mapDispatch, mergeProps) | ||
class Container extends Component { | ||
render() { | ||
return <Passthrough />; | ||
} | ||
} | ||
); | ||
} | ||
function AwesomeMap() { } | ||
expect(() => { | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
{makeContainer(() => 1, () => ({}), () => ({}))} | ||
</ProviderMock> | ||
); | ||
}).toThrow(/mapState/); | ||
expect(() => { | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
{makeContainer(() => 'hey', () => ({}), () => ({}))} | ||
</ProviderMock> | ||
); | ||
}).toThrow(/mapState/); | ||
expect(() => { | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
{makeContainer(() => new AwesomeMap(), () => ({}), () => ({}))} | ||
</ProviderMock> | ||
); | ||
}).toThrow(/mapState/); | ||
expect(() => { | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
{makeContainer(() => ({}), () => 1, () => ({}))} | ||
</ProviderMock> | ||
); | ||
}).toThrow(/mapDispatch/); | ||
expect(() => { | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
{makeContainer(() => ({}), () => 'hey', () => ({}))} | ||
</ProviderMock> | ||
); | ||
}).toThrow(/mapDispatch/); | ||
expect(() => { | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
{makeContainer(() => ({}), () => new AwesomeMap(), () => ({}))} | ||
</ProviderMock> | ||
); | ||
}).toThrow(/mapDispatch/); | ||
expect(() => { | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
{makeContainer(() => ({}), () => ({}), () => 1)} | ||
</ProviderMock> | ||
); | ||
}).toThrow(/mergeProps/); | ||
expect(() => { | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
{makeContainer(() => ({}), () => ({}), () => 'hey')} | ||
</ProviderMock> | ||
); | ||
}).toThrow(/mergeProps/); | ||
expect(() => { | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
{makeContainer(() => ({}), () => ({}), () => new AwesomeMap())} | ||
</ProviderMock> | ||
); | ||
}).toThrow(/mergeProps/); | ||
}); | ||
it('should recalculate the state and rebind the actions on hot update', () => { | ||
const store = createStore(() => {}); | ||
@connect( | ||
null, | ||
() => ({ scooby: 'doo' }) | ||
) | ||
class ContainerBefore extends Component { | ||
render() { | ||
return ( | ||
<Passthrough {...this.props} /> | ||
); | ||
} | ||
} | ||
@connect( | ||
() => ({ foo: 'baz' }), | ||
() => ({ scooby: 'foo' }) | ||
) | ||
class ContainerAfter extends Component { | ||
render() { | ||
return ( | ||
<Passthrough {...this.props} /> | ||
); | ||
} | ||
} | ||
@connect( | ||
() => ({ foo: 'bar' }), | ||
() => ({ scooby: 'boo' }) | ||
) | ||
class ContainerNext extends Component { | ||
render() { | ||
return ( | ||
<Passthrough {...this.props} /> | ||
); | ||
} | ||
} | ||
let container; | ||
TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<ContainerBefore ref={instance => container = instance} /> | ||
</ProviderMock> | ||
); | ||
const stub = TestUtils.findRenderedComponentWithType(container, Passthrough); | ||
expect(stub.props.foo).toEqual(undefined); | ||
expect(stub.props.scooby).toEqual('doo'); | ||
function imitateHotReloading(TargetClass, SourceClass) { | ||
// Crude imitation of hot reloading that does the job | ||
Object.keys(SourceClass.prototype).filter(key => | ||
typeof SourceClass.prototype[key] === 'function' | ||
).forEach(key => { | ||
if (key !== 'render') { | ||
TargetClass.prototype[key] = SourceClass.prototype[key]; | ||
} | ||
}); | ||
container.forceUpdate(); | ||
} | ||
imitateHotReloading(ContainerBefore, ContainerAfter); | ||
expect(stub.props.foo).toEqual('baz'); | ||
expect(stub.props.scooby).toEqual('foo'); | ||
imitateHotReloading(ContainerBefore, ContainerNext); | ||
expect(stub.props.foo).toEqual('bar'); | ||
expect(stub.props.scooby).toEqual('boo'); | ||
}); | ||
it('should set the displayName correctly', () => { | ||
expect(connect(state => state)( | ||
class Foo extends Component { | ||
render() { | ||
return <div />; | ||
} | ||
} | ||
).displayName).toBe('Connect(Foo)'); | ||
expect(connect(state => state)( | ||
createClass({ | ||
displayName: 'Bar', | ||
render() { | ||
return <div />; | ||
} | ||
}) | ||
).displayName).toBe('Connect(Bar)'); | ||
expect(connect(state => state)( | ||
createClass({ | ||
render() { | ||
return <div />; | ||
} | ||
}) | ||
).displayName).toBe('Connect(Component)'); | ||
}); | ||
it('should expose the wrapped component as WrappedComponent', () => { | ||
class Container extends Component { | ||
render() { | ||
return <Passthrough />; | ||
} | ||
} | ||
const decorator = connect(state => state); | ||
const decorated = decorator(Container); | ||
expect(decorated.DecoratedComponent).toBe(Container); | ||
expect(decorated.WrappedComponent).toBe(Container); | ||
}); | ||
it('should use the store from the props instead of from the context if present', () => { | ||
class Container extends Component { | ||
render() { | ||
return <Passthrough />; | ||
} | ||
} | ||
let actualState; | ||
const expectedState = { foos: {} }; | ||
const decorator = connect(state => { | ||
actualState = state; | ||
return {}; | ||
}); | ||
const Decorated = decorator(Container); | ||
const mockStore = { | ||
dispatch: () => {}, | ||
subscribe: () => {}, | ||
getState: () => expectedState | ||
}; | ||
TestUtils.renderIntoDocument(<Decorated store={mockStore} />); | ||
expect(actualState).toEqual(expectedState); | ||
}); | ||
it('should throw an error if the store is not in the props or context', () => { | ||
class Container extends Component { | ||
render() { | ||
return <Passthrough />; | ||
} | ||
} | ||
const decorator = connect(() => {}); | ||
const Decorated = decorator(Container); | ||
const expectedError = | ||
`Invariant Violation: Could not find "store" in either the context ` + | ||
`or props of "Connect(Container)". Either wrap the root component in a ` + | ||
`<Provider>, or explicitly pass "store" as a prop to "Connect(Container)".`; | ||
expect(() => TestUtils.renderIntoDocument(<Decorated />)).toThrow(e => { | ||
expect(e.message).toEqual(expectedError); | ||
return true; | ||
}); | ||
}); | ||
it('should return the instance of the wrapped component for use in calling child methods', () => { | ||
const store = createStore(() => ({})); | ||
const someData = { | ||
some: 'data' | ||
}; | ||
class Container extends Component { | ||
someInstanceMethod() { | ||
return someData; | ||
} | ||
render() { | ||
return <Passthrough />; | ||
} | ||
} | ||
const decorator = connect(state => state); | ||
const Decorated = decorator(Container); | ||
const tree = TestUtils.renderIntoDocument( | ||
<ProviderMock store={store}> | ||
<Decorated /> | ||
</ProviderMock> | ||
); | ||
const decorated = TestUtils.findRenderedComponentWithType(tree, Decorated); | ||
expect(() => decorated.someInstanceMethod()).toThrow(); | ||
expect(decorated.getWrappedInstance().someInstanceMethod()).toBe(someData); | ||
expect(decorated.refs.wrappedInstance.someInstanceMethod()).toBe(someData); | ||
}); | ||
}); | ||
}); |
import expect from 'expect'; | ||
import jsdomReact from './jsdomReact'; | ||
import React, { PropTypes, Component } from 'react/addons'; | ||
import jsdom from 'mocha-jsdom'; | ||
import React, { PropTypes, Component } from 'react'; | ||
import TestUtils from 'react-addons-test-utils'; | ||
import { createStore } from 'redux'; | ||
import { Provider } from '../../src/index'; | ||
import createProvider from '../../src/components/createProvider'; | ||
const { TestUtils } = React.addons; | ||
describe('React', () => { | ||
describe('Provider', () => { | ||
jsdomReact(); | ||
jsdom(); | ||
@@ -23,10 +23,148 @@ class Child extends Component { | ||
it('should not warn when using child-as-a-function before React 0.14', () => { | ||
const store = createStore(() => ({})); | ||
['0.13.0-beta', '0.13.0', '0.13.3'].forEach(version => { | ||
const LocalProvider = createProvider({ ...React, version }); | ||
let spy = expect.spyOn(console, 'error'); | ||
const tree = TestUtils.renderIntoDocument( | ||
<LocalProvider store={store}> | ||
{() => <Child />} | ||
</LocalProvider> | ||
); | ||
spy.destroy(); | ||
expect(spy.calls.length).toBe(0); | ||
spy = expect.spyOn(console, 'error'); | ||
tree.forceUpdate(); | ||
spy.destroy(); | ||
expect(spy.calls.length).toBe(0); | ||
}); | ||
}); | ||
it('should warn once when using a single element before React 0.14', () => { | ||
const store = createStore(() => ({})); | ||
['0.13.0-beta', '0.13.0', '0.13.3'].forEach(version => { | ||
const LocalProvider = createProvider({ ...React, version }); | ||
// Trick React into checking propTypes every time: | ||
LocalProvider.displayName = Math.random().toString(); | ||
let spy = expect.spyOn(console, 'error'); | ||
const tree = TestUtils.renderIntoDocument( | ||
<LocalProvider store={store}> | ||
<Child /> | ||
</LocalProvider> | ||
); | ||
spy.destroy(); | ||
expect(spy.calls.length).toBe(2); | ||
expect(spy.calls[0].arguments[0]).toMatch( | ||
/Invalid prop `children` of type `object` supplied to .*, expected `function`./ | ||
); | ||
expect(spy.calls[1].arguments[0]).toMatch( | ||
/With React 0.13, you need to wrap <Provider> child into a function. This restriction will be removed with React 0.14./ | ||
); | ||
spy = expect.spyOn(console, 'error'); | ||
tree.forceUpdate(); | ||
spy.destroy(); | ||
expect(spy.calls.length).toBe(0); | ||
}); | ||
}); | ||
it('should warn once when using child-as-a-function after React 0.14', () => { | ||
const store = createStore(() => ({})); | ||
['0.14.0-beta3', '0.14.0', '0.14.2', '0.15.0-beta', '1.0.0-beta', '1.0.0'].forEach(version => { | ||
const LocalProvider = createProvider({ ...React, version }); | ||
// Trick React into checking propTypes every time: | ||
LocalProvider.displayName = Math.random().toString(); | ||
let spy = expect.spyOn(console, 'error'); | ||
const tree = TestUtils.renderIntoDocument( | ||
<LocalProvider store={store}> | ||
{() => <Child />} | ||
</LocalProvider> | ||
); | ||
spy.destroy(); | ||
expect(spy.calls.length).toBe(2); | ||
expect(spy.calls[0].arguments[0]).toMatch( | ||
/Invalid prop `children` supplied to .*, expected a single ReactElement./ | ||
); | ||
expect(spy.calls[1].arguments[0]).toMatch( | ||
/With React 0.14 and later versions, you no longer need to wrap <Provider> child into a function./ | ||
); | ||
spy = expect.spyOn(console, 'error'); | ||
tree.forceUpdate(); | ||
spy.destroy(); | ||
expect(spy.calls.length).toBe(0); | ||
}); | ||
}); | ||
it('should enforce a single child', () => { | ||
const store = createStore(() => ({})); | ||
expect(() => TestUtils.renderIntoDocument( | ||
<Provider store={store}> | ||
<div /> | ||
</Provider> | ||
)).toNotThrow(); | ||
expect(() => TestUtils.renderIntoDocument( | ||
<Provider store={store}> | ||
</Provider> | ||
)).toThrow(/exactly one child/); | ||
expect(() => TestUtils.renderIntoDocument( | ||
<Provider store={store}> | ||
<div /> | ||
<div /> | ||
</Provider> | ||
)).toThrow(/exactly one child/); | ||
}); | ||
it('should enforce a single child when using function-as-a-child', () => { | ||
const store = createStore(() => ({})); | ||
expect(() => TestUtils.renderIntoDocument( | ||
<Provider store={store}> | ||
{() => <div />} | ||
</Provider> | ||
)).toNotThrow(); | ||
expect(() => TestUtils.renderIntoDocument( | ||
<Provider store={store}> | ||
{() => {}} | ||
</Provider> | ||
)).toThrow(/exactly one child/); | ||
}); | ||
it('should add the store to the child context', () => { | ||
const store = createStore({}); | ||
const store = createStore(() => ({})); | ||
const spy = expect.spyOn(console, 'error'); | ||
const tree = TestUtils.renderIntoDocument( | ||
<Provider store={store}> | ||
<Child /> | ||
</Provider> | ||
); | ||
spy.destroy(); | ||
expect(spy.calls.length).toBe(0); | ||
const child = TestUtils.findRenderedComponentWithType(tree, Child); | ||
expect(child.context.store).toBe(store); | ||
}); | ||
it('should add the store to the child context with function-as-a-child', () => { | ||
const store = createStore(() => ({})); | ||
const spy = expect.spyOn(console, 'error'); | ||
const tree = TestUtils.renderIntoDocument( | ||
<Provider store={store}> | ||
{() => <Child />} | ||
</Provider> | ||
); | ||
spy.destroy(); | ||
expect(spy.calls.length).toBe(0); | ||
@@ -40,3 +178,3 @@ const child = TestUtils.findRenderedComponentWithType(tree, Child); | ||
const store2 = createStore((state = 10) => state * 2); | ||
const spy = expect.createSpy(() => {}); | ||
const spy = expect.createSpy(() => ({})); | ||
@@ -49,3 +187,3 @@ class ProviderContainer extends Component { | ||
<Provider store={this.state.store}> | ||
{() => <Child />} | ||
<Child /> | ||
</Provider> | ||
@@ -52,0 +190,0 @@ ); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
116867
2
2217
0
1
474
18
30
4