Skip to content

Commit 90f427b

Browse files
committed
Disable state shape checks in production
1 parent 71de169 commit 90f427b

File tree

4 files changed

+164
-82
lines changed

4 files changed

+164
-82
lines changed

src/components/Provider.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Component, PropTypes, Children } from 'react'
22
import storeShape from '../utils/storeShape'
3+
import warning from '../utils/warning'
34

45
let didWarnAboutReceivingStore = false
56
function warnAboutReceivingStore() {
@@ -10,7 +11,7 @@ function warnAboutReceivingStore() {
1011

1112
/* eslint-disable no-console */
1213
if (typeof console !== 'undefined' && typeof console.error === 'function') {
13-
console.error(
14+
warning(
1415
'<Provider> does not support changing `store` on the fly. ' +
1516
'It is most likely that you see this error because you updated to ' +
1617
'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' +

src/components/connect.js

+42-18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Component, createElement } from 'react'
22
import storeShape from '../utils/storeShape'
33
import shallowEqual from '../utils/shallowEqual'
44
import wrapActionCreators from '../utils/wrapActionCreators'
5+
import warning from '../utils/warning'
56
import isPlainObject from 'lodash/isPlainObject'
67
import hoistStatics from 'hoist-non-react-statics'
78
import invariant from 'invariant'
@@ -24,9 +25,15 @@ let nextVersion = 0
2425
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
2526
const shouldSubscribe = Boolean(mapStateToProps)
2627
const mapState = mapStateToProps || defaultMapStateToProps
27-
const mapDispatch = isPlainObject(mapDispatchToProps) ?
28-
wrapActionCreators(mapDispatchToProps) :
29-
mapDispatchToProps || defaultMapDispatchToProps
28+
29+
let mapDispatch
30+
if (typeof mapDispatchToProps === 'function') {
31+
mapDispatch = mapDispatchToProps
32+
} else if (!mapDispatchToProps) {
33+
mapDispatch = defaultMapDispatchToProps
34+
} else {
35+
mapDispatch = wrapActionCreators(mapDispatchToProps)
36+
}
3037

3138
const finalMergeProps = mergeProps || defaultMergeProps
3239
const { pure = true, withRef = false } = options
@@ -39,19 +46,20 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
3946
const connectDisplayName = `Connect(${getDisplayName(WrappedComponent)})`
4047

4148
function checkStateShape(props, methodName) {
42-
invariant(
43-
isPlainObject(props),
44-
'`%s %s` must return an object. Instead received %s.',
45-
connectDisplayName,
46-
methodName,
47-
props
48-
)
49-
return props
49+
if (!isPlainObject(props)) {
50+
warning(
51+
`${methodName}() in ${connectDisplayName} must return a plain object. ` +
52+
`Instead received ${props}.`
53+
)
54+
}
5055
}
5156

5257
function computeMergedProps(stateProps, dispatchProps, parentProps) {
5358
const mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps)
54-
return checkStateShape(mergedProps, 'mergeProps')
59+
if (process.env.NODE_ENV !== 'production') {
60+
checkStateShape(mergedProps, 'mergeProps')
61+
}
62+
return mergedProps
5563
}
5664

5765
class Connect extends Component {
@@ -86,7 +94,10 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
8694
this.finalMapStateToProps(state, props) :
8795
this.finalMapStateToProps(state)
8896

89-
return checkStateShape(stateProps, 'mapStateToProps')
97+
if (process.env.NODE_ENV !== 'production') {
98+
checkStateShape(stateProps, 'mapStateToProps')
99+
}
100+
return stateProps
90101
}
91102

92103
configureFinalMapState(store, props) {
@@ -96,9 +107,14 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
96107
this.finalMapStateToProps = isFactory ? mappedState : mapState
97108
this.doStatePropsDependOnOwnProps = this.finalMapStateToProps.length !== 1
98109

99-
return isFactory ?
100-
this.computeStateProps(store, props) :
110+
if (isFactory) {
111+
return this.computeStateProps(store, props)
112+
}
113+
114+
if (process.env.NODE_ENV !== 'production') {
101115
checkStateShape(mappedState, 'mapStateToProps')
116+
}
117+
return mappedState
102118
}
103119

104120
computeDispatchProps(store, props) {
@@ -111,7 +127,10 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
111127
this.finalMapDispatchToProps(dispatch, props) :
112128
this.finalMapDispatchToProps(dispatch)
113129

114-
return checkStateShape(dispatchProps, 'mapDispatchToProps')
130+
if (process.env.NODE_ENV !== 'production') {
131+
checkStateShape(dispatchProps, 'mapDispatchToProps')
132+
}
133+
return dispatchProps
115134
}
116135

117136
configureFinalMapDispatch(store, props) {
@@ -121,9 +140,14 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
121140
this.finalMapDispatchToProps = isFactory ? mappedDispatch : mapDispatch
122141
this.doDispatchPropsDependOnOwnProps = this.finalMapDispatchToProps.length !== 1
123142

124-
return isFactory ?
125-
this.computeDispatchProps(store, props) :
143+
if (isFactory) {
144+
return this.computeDispatchProps(store, props)
145+
}
146+
147+
if (process.env.NODE_ENV !== 'production') {
126148
checkStateShape(mappedDispatch, 'mapDispatchToProps')
149+
}
150+
return mappedDispatch
127151
}
128152

129153
updateStatePropsIfNeeded() {

src/utils/warning.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Prints a warning in the console if it exists.
3+
*
4+
* @param {String} message The warning message.
5+
* @returns {void}
6+
*/
7+
export default function warning(message) {
8+
/* eslint-disable no-console */
9+
if (typeof console !== 'undefined' && typeof console.error === 'function') {
10+
console.error(message);
11+
}
12+
/* eslint-enable no-console */
13+
try {
14+
// This error was thrown as a convenience so that you can use this stack
15+
// to find the callsite that caused this warning to fire.
16+
throw new Error(message);
17+
/* eslint-disable no-empty */
18+
} catch (e) {}
19+
/* eslint-enable no-empty */
20+
}

test/components/connect.spec.js

+100-63
Original file line numberDiff line numberDiff line change
@@ -998,77 +998,114 @@ describe('React', () => {
998998

999999
function AwesomeMap() { }
10001000

1001-
expect(() => {
1002-
TestUtils.renderIntoDocument(
1003-
<ProviderMock store={store}>
1004-
{makeContainer(() => 1, () => ({}), () => ({}))}
1005-
</ProviderMock>
1006-
)
1007-
}).toThrow(/Connect\(Container\) mapState/)
1001+
let spy = expect.spyOn(console, 'error')
1002+
TestUtils.renderIntoDocument(
1003+
<ProviderMock store={store}>
1004+
{makeContainer(() => 1, () => ({}), () => ({}))}
1005+
</ProviderMock>
1006+
)
1007+
expect(spy.calls.length).toBe(1)
1008+
expect(spy.calls[0].arguments[0]).toMatch(
1009+
/mapStateToProps\(\) in Connect\(Container\) must return a plain object/
1010+
)
1011+
spy.destroy()
10081012

1009-
expect(() => {
1010-
TestUtils.renderIntoDocument(
1011-
<ProviderMock store={store}>
1012-
{makeContainer(() => 'hey', () => ({}), () => ({}))}
1013-
</ProviderMock>
1014-
)
1015-
}).toThrow(/Connect\(Container\) mapState/)
1013+
spy = expect.spyOn(console, 'error')
1014+
TestUtils.renderIntoDocument(
1015+
<ProviderMock store={store}>
1016+
{makeContainer(() => 'hey', () => ({}), () => ({}))}
1017+
</ProviderMock>
1018+
)
1019+
expect(spy.calls.length).toBe(1)
1020+
expect(spy.calls[0].arguments[0]).toMatch(
1021+
/mapStateToProps\(\) in Connect\(Container\) must return a plain object/
1022+
)
1023+
spy.destroy()
10161024

1017-
expect(() => {
1018-
TestUtils.renderIntoDocument(
1019-
<ProviderMock store={store}>
1020-
{makeContainer(() => new AwesomeMap(), () => ({}), () => ({}))}
1021-
</ProviderMock>
1022-
)
1023-
}).toThrow(/Connect\(Container\) mapState/)
1025+
spy = expect.spyOn(console, 'error')
1026+
TestUtils.renderIntoDocument(
1027+
<ProviderMock store={store}>
1028+
{makeContainer(() => new AwesomeMap(), () => ({}), () => ({}))}
1029+
</ProviderMock>
1030+
)
1031+
expect(spy.calls.length).toBe(1)
1032+
expect(spy.calls[0].arguments[0]).toMatch(
1033+
/mapStateToProps\(\) in Connect\(Container\) must return a plain object/
1034+
)
1035+
spy.destroy()
10241036

1025-
expect(() => {
1026-
TestUtils.renderIntoDocument(
1027-
<ProviderMock store={store}>
1028-
{makeContainer(() => ({}), () => 1, () => ({}))}
1029-
</ProviderMock>
1030-
)
1031-
}).toThrow(/Connect\(Container\) mapDispatch/)
1037+
spy = expect.spyOn(console, 'error')
1038+
TestUtils.renderIntoDocument(
1039+
<ProviderMock store={store}>
1040+
{makeContainer(() => ({}), () => 1, () => ({}))}
1041+
</ProviderMock>
1042+
)
1043+
expect(spy.calls.length).toBe(1)
1044+
expect(spy.calls[0].arguments[0]).toMatch(
1045+
/mapDispatchToProps\(\) in Connect\(Container\) must return a plain object/
1046+
)
1047+
spy.destroy()
10321048

1033-
expect(() => {
1034-
TestUtils.renderIntoDocument(
1035-
<ProviderMock store={store}>
1036-
{makeContainer(() => ({}), () => 'hey', () => ({}))}
1037-
</ProviderMock>
1038-
)
1039-
}).toThrow(/Connect\(Container\) mapDispatch/)
1049+
spy = expect.spyOn(console, 'error')
1050+
TestUtils.renderIntoDocument(
1051+
<ProviderMock store={store}>
1052+
{makeContainer(() => ({}), () => 'hey', () => ({}))}
1053+
</ProviderMock>
1054+
)
1055+
expect(spy.calls.length).toBe(1)
1056+
expect(spy.calls[0].arguments[0]).toMatch(
1057+
/mapDispatchToProps\(\) in Connect\(Container\) must return a plain object/
1058+
)
1059+
spy.destroy()
10401060

1041-
expect(() => {
1042-
TestUtils.renderIntoDocument(
1043-
<ProviderMock store={store}>
1044-
{makeContainer(() => ({}), () => new AwesomeMap(), () => ({}))}
1045-
</ProviderMock>
1046-
)
1047-
}).toThrow(/Connect\(Container\) mapDispatch/)
1061+
spy = expect.spyOn(console, 'error')
1062+
TestUtils.renderIntoDocument(
1063+
<ProviderMock store={store}>
1064+
{makeContainer(() => ({}), () => new AwesomeMap(), () => ({}))}
1065+
</ProviderMock>
1066+
)
1067+
expect(spy.calls.length).toBe(1)
1068+
expect(spy.calls[0].arguments[0]).toMatch(
1069+
/mapDispatchToProps\(\) in Connect\(Container\) must return a plain object/
1070+
)
1071+
spy.destroy()
10481072

1049-
expect(() => {
1050-
TestUtils.renderIntoDocument(
1051-
<ProviderMock store={store}>
1052-
{makeContainer(() => ({}), () => ({}), () => 1)}
1053-
</ProviderMock>
1054-
)
1055-
}).toThrow(/Connect\(Container\) mergeProps/)
1073+
spy = expect.spyOn(console, 'error')
1074+
TestUtils.renderIntoDocument(
1075+
<ProviderMock store={store}>
1076+
{makeContainer(() => ({}), () => ({}), () => 1)}
1077+
</ProviderMock>
1078+
)
1079+
expect(spy.calls.length).toBe(1)
1080+
expect(spy.calls[0].arguments[0]).toMatch(
1081+
/mergeProps\(\) in Connect\(Container\) must return a plain object/
1082+
)
1083+
spy.destroy()
10561084

1057-
expect(() => {
1058-
TestUtils.renderIntoDocument(
1059-
<ProviderMock store={store}>
1060-
{makeContainer(() => ({}), () => ({}), () => 'hey')}
1061-
</ProviderMock>
1062-
)
1063-
}).toThrow(/Connect\(Container\) mergeProps/)
10641085

1065-
expect(() => {
1066-
TestUtils.renderIntoDocument(
1067-
<ProviderMock store={store}>
1068-
{makeContainer(() => ({}), () => ({}), () => new AwesomeMap())}
1069-
</ProviderMock>
1070-
)
1071-
}).toThrow(/Connect\(Container\) mergeProps/)
1086+
spy = expect.spyOn(console, 'error')
1087+
TestUtils.renderIntoDocument(
1088+
<ProviderMock store={store}>
1089+
{makeContainer(() => ({}), () => ({}), () => 'hey')}
1090+
</ProviderMock>
1091+
)
1092+
expect(spy.calls.length).toBe(1)
1093+
expect(spy.calls[0].arguments[0]).toMatch(
1094+
/mergeProps\(\) in Connect\(Container\) must return a plain object/
1095+
)
1096+
spy.destroy()
1097+
1098+
spy = expect.spyOn(console, 'error')
1099+
TestUtils.renderIntoDocument(
1100+
<ProviderMock store={store}>
1101+
{makeContainer(() => ({}), () => ({}), () => new AwesomeMap())}
1102+
</ProviderMock>
1103+
)
1104+
expect(spy.calls.length).toBe(1)
1105+
expect(spy.calls[0].arguments[0]).toMatch(
1106+
/mergeProps\(\) in Connect\(Container\) must return a plain object/
1107+
)
1108+
spy.destroy()
10721109
})
10731110

10741111
it('should recalculate the state and rebind the actions on hot update', () => {

0 commit comments

Comments
 (0)