diff --git a/src/components/connect.js b/src/components/connect.js index e2641b0d1..49c672798 100644 --- a/src/components/connect.js +++ b/src/components/connect.js @@ -219,6 +219,7 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, this.mergedProps = null this.haveOwnPropsChanged = true this.hasStoreStateChanged = true + this.haveStatePropsBeenPrecalculated = false this.renderedElement = null this.finalMapDispatchToProps = null this.finalMapStateToProps = null @@ -229,13 +230,21 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, return } - const prevStoreState = this.state.storeState const storeState = this.store.getState() + const prevStoreState = this.state.storeState + if (pure && prevStoreState === storeState) { + return + } - if (!pure || prevStoreState !== storeState) { - this.hasStoreStateChanged = true - this.setState({ storeState }) + if (pure && !this.doStatePropsDependOnOwnProps) { + if (!this.updateStatePropsIfNeeded()) { + return + } + this.haveStatePropsBeenPrecalculated = true } + + this.hasStoreStateChanged = true + this.setState({ storeState }) } getWrappedInstance() { @@ -251,11 +260,13 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, const { haveOwnPropsChanged, hasStoreStateChanged, + haveStatePropsBeenPrecalculated, renderedElement } = this this.haveOwnPropsChanged = false this.hasStoreStateChanged = false + this.haveStatePropsBeenPrecalculated = false let shouldUpdateStateProps = true let shouldUpdateDispatchProps = true @@ -269,7 +280,9 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, let haveStatePropsChanged = false let haveDispatchPropsChanged = false - if (shouldUpdateStateProps) { + if (haveStatePropsBeenPrecalculated) { + haveStatePropsChanged = true + } else if (shouldUpdateStateProps) { haveStatePropsChanged = this.updateStatePropsIfNeeded() } if (shouldUpdateDispatchProps) { diff --git a/test/components/connect.spec.js b/test/components/connect.spec.js index e813e8ac1..d2dff98ac 100644 --- a/test/components/connect.spec.js +++ b/test/components/connect.spec.js @@ -1082,7 +1082,6 @@ describe('React', () => { ) spy.destroy() - spy = expect.spyOn(console, 'error') TestUtils.renderIntoDocument( @@ -1547,6 +1546,51 @@ describe('React', () => { expect(renderCalls).toBe(1) }) + it('should bail out early if mapState does not depend on props', () => { + const store = createStore(stringBuilder) + let renderCalls = 0 + let mapStateCalls = 0 + + @connect(state => { + mapStateCalls++ + return state === 'aaa' ? { change: 1 } : {} + }) + class Container extends Component { + render() { + renderCalls++ + return + } + } + + TestUtils.renderIntoDocument( + + + + ) + + expect(renderCalls).toBe(1) + expect(mapStateCalls).toBe(1) + + const spy = expect.spyOn(Container.prototype, 'setState').andCallThrough() + + store.dispatch({ type: 'APPEND', body: 'a' }) + expect(mapStateCalls).toBe(2) + expect(renderCalls).toBe(1) + expect(spy.calls.length).toBe(0) + + store.dispatch({ type: 'APPEND', body: 'a' }) + expect(mapStateCalls).toBe(3) + expect(renderCalls).toBe(1) + expect(spy.calls.length).toBe(0) + + store.dispatch({ type: 'APPEND', body: 'a' }) + expect(mapStateCalls).toBe(4) + expect(renderCalls).toBe(2) + expect(spy.calls.length).toBe(1) + + spy.destroy() + }) + it('should allow providing a factory function to mapStateToProps', () => { let updatedCount = 0 let memoizedReturnCount = 0