Skip to content

Commit 388c050

Browse files
committed
Bail out if stateProps can be calculated early and did not change
Fixes #1437, #300
1 parent a0947cf commit 388c050

File tree

2 files changed

+63
-6
lines changed

2 files changed

+63
-6
lines changed

src/components/connect.js

+18-5
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
219219
this.mergedProps = null
220220
this.haveOwnPropsChanged = true
221221
this.hasStoreStateChanged = true
222+
this.haveStatePropsBeenPrecalculated = false
222223
this.renderedElement = null
223224
this.finalMapDispatchToProps = null
224225
this.finalMapStateToProps = null
@@ -229,13 +230,21 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
229230
return
230231
}
231232

232-
const prevStoreState = this.state.storeState
233233
const storeState = this.store.getState()
234+
const prevStoreState = this.state.storeState
235+
if (pure && prevStoreState === storeState) {
236+
return
237+
}
234238

235-
if (!pure || prevStoreState !== storeState) {
236-
this.hasStoreStateChanged = true
237-
this.setState({ storeState })
239+
if (pure && !this.doStatePropsDependOnOwnProps) {
240+
if (!this.updateStatePropsIfNeeded()) {
241+
return
242+
}
243+
this.haveStatePropsBeenPrecalculated = true
238244
}
245+
246+
this.hasStoreStateChanged = true
247+
this.setState({ storeState })
239248
}
240249

241250
getWrappedInstance() {
@@ -251,11 +260,13 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
251260
const {
252261
haveOwnPropsChanged,
253262
hasStoreStateChanged,
263+
haveStatePropsBeenPrecalculated,
254264
renderedElement
255265
} = this
256266

257267
this.haveOwnPropsChanged = false
258268
this.hasStoreStateChanged = false
269+
this.haveStatePropsBeenPrecalculated = false
259270

260271
let shouldUpdateStateProps = true
261272
let shouldUpdateDispatchProps = true
@@ -269,7 +280,9 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
269280

270281
let haveStatePropsChanged = false
271282
let haveDispatchPropsChanged = false
272-
if (shouldUpdateStateProps) {
283+
if (haveStatePropsBeenPrecalculated) {
284+
haveStatePropsChanged = true
285+
} else if (shouldUpdateStateProps) {
273286
haveStatePropsChanged = this.updateStatePropsIfNeeded()
274287
}
275288
if (shouldUpdateDispatchProps) {

test/components/connect.spec.js

+45-1
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,6 @@ describe('React', () => {
10821082
)
10831083
spy.destroy()
10841084

1085-
10861085
spy = expect.spyOn(console, 'error')
10871086
TestUtils.renderIntoDocument(
10881087
<ProviderMock store={store}>
@@ -1547,6 +1546,51 @@ describe('React', () => {
15471546
expect(renderCalls).toBe(1)
15481547
})
15491548

1549+
it('should bail out early if mapState does not depend on props', () => {
1550+
const store = createStore(stringBuilder)
1551+
let renderCalls = 0
1552+
let mapStateCalls = 0
1553+
1554+
@connect(state => {
1555+
mapStateCalls++
1556+
return state === 'aaa' ? { change: 1 } : {}
1557+
})
1558+
class Container extends Component {
1559+
render() {
1560+
renderCalls++
1561+
return <Passthrough {...this.props} />
1562+
}
1563+
}
1564+
1565+
TestUtils.renderIntoDocument(
1566+
<ProviderMock store={store}>
1567+
<Container />
1568+
</ProviderMock>
1569+
)
1570+
1571+
expect(renderCalls).toBe(1)
1572+
expect(mapStateCalls).toBe(1)
1573+
1574+
const spy = expect.spyOn(Container.prototype, 'setState').andCallThrough()
1575+
1576+
store.dispatch({ type: 'APPEND', body: 'a' })
1577+
expect(mapStateCalls).toBe(2)
1578+
expect(renderCalls).toBe(1)
1579+
expect(spy.calls.length).toBe(0)
1580+
1581+
store.dispatch({ type: 'APPEND', body: 'a' })
1582+
expect(mapStateCalls).toBe(3)
1583+
expect(renderCalls).toBe(1)
1584+
expect(spy.calls.length).toBe(0)
1585+
1586+
store.dispatch({ type: 'APPEND', body: 'a' })
1587+
expect(mapStateCalls).toBe(4)
1588+
expect(renderCalls).toBe(2)
1589+
expect(spy.calls.length).toBe(1)
1590+
1591+
spy.destroy()
1592+
})
1593+
15501594
it('should allow providing a factory function to mapStateToProps', () => {
15511595
let updatedCount = 0
15521596
let memoizedReturnCount = 0

0 commit comments

Comments
 (0)