@@ -1147,5 +1147,103 @@ describe('React', () => {
1147
1147
wrapper . setState ( { value : 1 } ) ;
1148
1148
expect ( target . props . statefulValue ) . toEqual ( 1 ) ;
1149
1149
} ) ;
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
+ } ) ;
1150
1248
} ) ;
1151
1249
} ) ;
0 commit comments