@@ -1147,5 +1147,103 @@ describe('React', () => {
11471147 wrapper . setState ( { value : 1 } ) ;
11481148 expect ( target . props . statefulValue ) . toEqual ( 1 ) ;
11491149 } ) ;
1150+
1151+ it ( 'should pass state consistently to mapState' , ( ) => {
1152+ const store = createStore ( stringBuilder ) ;
1153+
1154+ store . dispatch ( { type : 'APPEND' , body : 'a' } ) ;
1155+ let childMapStateInvokes = 0 ;
1156+
1157+ @connect ( state => ( { state } ) )
1158+ class Container extends Component {
1159+
1160+ emitChange ( ) {
1161+ store . dispatch ( { type : 'APPEND' , body : 'b' } ) ;
1162+ }
1163+
1164+ render ( ) {
1165+ return (
1166+ < div >
1167+ < button ref = "button" onClick = { this . emitChange . bind ( this ) } > change</ button >
1168+ < ChildContainer parentState = { this . props . state } />
1169+ </ div >
1170+ ) ;
1171+ }
1172+ }
1173+
1174+ @connect ( ( state , parentProps ) => {
1175+ childMapStateInvokes ++ ;
1176+ // The state from parent props should always be consistent with the current state
1177+ expect ( state ) . toEqual ( parentProps . parentState ) ;
1178+ return { } ;
1179+ } )
1180+ class ChildContainer extends Component {
1181+ render ( ) {
1182+ return < Passthrough { ...this . props } /> ;
1183+ }
1184+ }
1185+
1186+ const tree = TestUtils . renderIntoDocument (
1187+ < ProviderMock store = { store } >
1188+ < Container />
1189+ </ ProviderMock >
1190+ ) ;
1191+
1192+ expect ( childMapStateInvokes ) . toBe ( 2 ) ;
1193+
1194+ // The store state stays consistent when setState calls are batched
1195+ ReactDOM . unstable_batchedUpdates ( ( ) => {
1196+ store . dispatch ( { type : 'APPEND' , body : 'c' } ) ;
1197+ } ) ;
1198+ expect ( childMapStateInvokes ) . toBe ( 3 ) ;
1199+
1200+ // setState calls DOM handlers are batched
1201+ const container = TestUtils . findRenderedComponentWithType ( tree , Container ) ;
1202+ const node = React . findDOMNode ( container . getWrappedInstance ( ) . refs . button ) ;
1203+ TestUtils . Simulate . click ( node ) ;
1204+ expect ( childMapStateInvokes ) . toBe ( 4 ) ;
1205+
1206+ // In future all setState calls will be batched[1]. Uncomment when it
1207+ // happens. For now redux-batched-updates middleware can be used as
1208+ // workaround this.
1209+ //
1210+ // [1]: https://twitter.com/sebmarkbage/status/642366976824864768
1211+ //
1212+ // store.dispatch({ type: 'APPEND', body: 'd'});
1213+ // expect(childMapStateInvokes).toBe(5);
1214+ } ) ;
1215+
1216+ it ( 'should not render the wrapped component when mapState does not produce change' , ( ) => {
1217+ const store = createStore ( stringBuilder ) ;
1218+ let renderCalls = 0 ;
1219+ let mapStateCalls = 0 ;
1220+
1221+ @connect ( ( ) => {
1222+ mapStateCalls ++ ;
1223+ return { } ; // no change!
1224+ } )
1225+ class Container extends Component {
1226+ render ( ) {
1227+ renderCalls ++ ;
1228+ return < Passthrough { ...this . props } /> ;
1229+ }
1230+ }
1231+
1232+ TestUtils . renderIntoDocument (
1233+ < ProviderMock store = { store } >
1234+ < Container />
1235+ </ ProviderMock >
1236+ ) ;
1237+
1238+ expect ( renderCalls ) . toBe ( 1 ) ;
1239+ expect ( mapStateCalls ) . toBe ( 2 ) ;
1240+
1241+ store . dispatch ( { type : 'APPEND' , body : 'a' } ) ;
1242+
1243+ // After store a change mapState has been called
1244+ expect ( mapStateCalls ) . toBe ( 3 ) ;
1245+ // But render is not because it did not make any actual changes
1246+ expect ( renderCalls ) . toBe ( 1 ) ;
1247+ } ) ;
11501248 } ) ;
11511249} ) ;
0 commit comments