1
1
/*eslint-disable react/prop-types*/
2
2
3
- import React , { Component } from 'react'
3
+ import React , { Component , Dispatch } from 'react'
4
4
import ReactDOM from 'react-dom'
5
5
import { createStore } from 'redux'
6
6
import { Provider , connect , ReactReduxContext } from '../../src/index'
7
7
import * as rtl from '@testing-library/react'
8
+ import type { ReactReduxContextValue } from '../../src'
9
+ import type { Store } from 'redux'
10
+
8
11
import '@testing-library/jest-dom/extend-expect'
9
12
10
13
const createExampleTextReducer =
@@ -21,7 +24,8 @@ describe('React', () => {
21
24
render ( ) {
22
25
return (
23
26
< ReactReduxContext . Consumer >
24
- { ( { store } ) => {
27
+ { ( props ) => {
28
+ let { store } = props as ReactReduxContextValue
25
29
let text = ''
26
30
27
31
if ( store ) {
@@ -46,10 +50,6 @@ describe('React', () => {
46
50
it ( 'should not enforce a single child' , ( ) => {
47
51
const store = createStore ( ( ) => ( { } ) )
48
52
49
- // Ignore propTypes warnings
50
- const propTypes = Provider . propTypes
51
- Provider . propTypes = { }
52
-
53
53
const spy = jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } )
54
54
55
55
expect ( ( ) =>
@@ -59,7 +59,7 @@ describe('React', () => {
59
59
</ Provider >
60
60
)
61
61
) . not . toThrow ( )
62
-
62
+ // @ts -expect-error
63
63
expect ( ( ) => rtl . render ( < Provider store = { store } /> ) ) . not . toThrow (
64
64
/ c h i l d r e n w i t h e x a c t l y o n e c h i l d /
65
65
)
@@ -73,7 +73,6 @@ describe('React', () => {
73
73
)
74
74
) . not . toThrow ( / a s i n g l e R e a c t e l e m e n t c h i l d / )
75
75
spy . mockRestore ( )
76
- Provider . propTypes = propTypes
77
76
} )
78
77
79
78
it ( 'should add the store to context' , ( ) => {
@@ -94,14 +93,18 @@ describe('React', () => {
94
93
} )
95
94
96
95
it ( 'accepts new store in props' , ( ) => {
97
- const store1 = createStore ( ( state = 10 ) => state + 1 )
98
- const store2 = createStore ( ( state = 10 ) => state * 2 )
99
- const store3 = createStore ( ( state = 10 ) => state * state + 1 )
100
-
101
- let externalSetState
102
- class ProviderContainer extends Component {
103
- constructor ( ) {
104
- super ( )
96
+ const store1 = createStore ( ( state : number = 10 ) => state + 1 )
97
+ const store2 = createStore ( ( state : number = 10 ) => state * 2 )
98
+ const store3 = createStore ( ( state : number = 10 ) => state * state + 1 )
99
+
100
+ interface StateType {
101
+ store : Store
102
+ }
103
+
104
+ let externalSetState : Dispatch < StateType >
105
+ class ProviderContainer extends Component < unknown , StateType > {
106
+ constructor ( props : { } ) {
107
+ super ( props )
105
108
this . state = { store : store1 }
106
109
externalSetState = this . setState . bind ( this )
107
110
}
@@ -156,33 +159,47 @@ describe('React', () => {
156
159
} )
157
160
158
161
it ( 'should handle subscriptions correctly when there is nested Providers' , ( ) => {
159
- const reducer = ( state = 0 , action ) =>
162
+ interface ActionType {
163
+ type : string
164
+ }
165
+ interface TStateProps {
166
+ count : number
167
+ }
168
+ const reducer = ( state = 0 , action : ActionType ) =>
160
169
action . type === 'INC' ? state + 1 : state
161
170
162
171
const innerStore = createStore ( reducer )
163
- const innerMapStateToProps = jest . fn ( ( state ) => ( { count : state } ) )
164
- @connect ( innerMapStateToProps )
165
- class Inner extends Component {
166
- render ( ) {
172
+ const innerMapStateToProps = jest . fn < TStateProps , [ number ] > ( ( state ) => ( {
173
+ count : state ,
174
+ } ) )
175
+ class Inner extends Component < TStateProps > {
176
+ render ( ) : JSX . Element {
167
177
return < div > { this . props . count } </ div >
168
178
}
169
179
}
170
180
181
+ const WrapperInner = connect < TStateProps , unknown , unknown , number > (
182
+ innerMapStateToProps
183
+ ) ( Inner )
184
+
171
185
const outerStore = createStore ( reducer )
172
- @connect ( ( state ) => ( { count : state } ) )
173
186
class Outer extends Component {
174
187
render ( ) {
175
188
return (
176
189
< Provider store = { innerStore } >
177
- < Inner />
190
+ < WrapperInner />
178
191
</ Provider >
179
192
)
180
193
}
181
194
}
182
195
196
+ const WrapperOuter = connect < TStateProps , unknown , unknown , number > (
197
+ ( state ) => ( { count : state } )
198
+ ) ( Outer )
199
+
183
200
rtl . render (
184
201
< Provider store = { outerStore } >
185
- < Outer />
202
+ < WrapperOuter />
186
203
</ Provider >
187
204
)
188
205
expect ( innerMapStateToProps ) . toHaveBeenCalledTimes ( 1 )
@@ -195,50 +212,69 @@ describe('React', () => {
195
212
} )
196
213
197
214
it ( 'should pass state consistently to mapState' , ( ) => {
198
- function stringBuilder ( prev = '' , action ) {
215
+ interface ActionType {
216
+ type : string
217
+ body : string
218
+ }
219
+ function stringBuilder ( prev = '' , action : ActionType ) {
199
220
return action . type === 'APPEND' ? prev + action . body : prev
200
221
}
201
222
202
- const store = createStore ( stringBuilder )
223
+ const store : Store = createStore ( stringBuilder )
203
224
204
225
rtl . act ( ( ) => {
205
226
store . dispatch ( { type : 'APPEND' , body : 'a' } )
206
227
} )
207
228
208
229
let childMapStateInvokes = 0
209
230
210
- @connect ( ( state ) => ( { state } ) )
211
- class Container extends Component {
212
- emitChange ( ) {
213
- store . dispatch ( { type : 'APPEND' , body : 'b' } )
214
- }
231
+ const childCalls : Array < Array < string > > = [ ]
215
232
233
+ interface ChildContainerProps {
234
+ parentState : string
235
+ }
236
+ class ChildContainer extends Component < ChildContainerProps > {
216
237
render ( ) {
217
- return (
218
- < div >
219
- < button onClick = { this . emitChange . bind ( this ) } > change</ button >
220
- < ChildContainer parentState = { this . props . state } />
221
- </ div >
222
- )
238
+ return < div />
223
239
}
224
240
}
225
241
226
- const childCalls = [ ]
227
- @connect ( ( state , parentProps ) => {
242
+ const WrapperChildrenContainer = connect <
243
+ { } ,
244
+ unknown ,
245
+ ChildContainerProps ,
246
+ string
247
+ > ( ( state , parentProps ) => {
228
248
childMapStateInvokes ++
229
249
childCalls . push ( [ state , parentProps . parentState ] )
230
250
// The state from parent props should always be consistent with the current state
231
251
return { }
232
- } )
233
- class ChildContainer extends Component {
252
+ } ) ( ChildContainer )
253
+
254
+ interface TStateProps {
255
+ state : string
256
+ }
257
+ class Container extends Component < TStateProps > {
258
+ emitChange ( ) {
259
+ store . dispatch ( { type : 'APPEND' , body : 'b' } )
260
+ }
261
+
234
262
render ( ) {
235
- return < div />
263
+ return (
264
+ < div >
265
+ < button onClick = { this . emitChange . bind ( this ) } > change</ button >
266
+ < WrapperChildrenContainer parentState = { this . props . state } />
267
+ </ div >
268
+ )
236
269
}
237
270
}
271
+ const WrapperContainer = connect < TStateProps , unknown , unknown , string > (
272
+ ( state ) => ( { state } )
273
+ ) ( Container )
238
274
239
275
const tester = rtl . render (
240
276
< Provider store = { store } >
241
- < Container />
277
+ < WrapperContainer />
242
278
</ Provider >
243
279
)
244
280
@@ -320,37 +356,52 @@ describe('React', () => {
320
356
} )
321
357
322
358
it ( 'should handle store and children change in a the same render' , ( ) => {
359
+ interface PropsType {
360
+ value : string
361
+ }
362
+ interface StateType {
363
+ nestedA : PropsType
364
+ nestedB : PropsType
365
+ }
323
366
const reducerA = ( state = { nestedA : { value : 'expectedA' } } ) => state
324
367
const reducerB = ( state = { nestedB : { value : 'expectedB' } } ) => state
325
368
326
369
const storeA = createStore ( reducerA )
327
370
const storeB = createStore ( reducerB )
328
371
329
- @connect ( ( state ) => ( { value : state . nestedA . value } ) )
330
- class ComponentA extends Component {
372
+ class ComponentA extends Component < PropsType > {
331
373
render ( ) {
332
374
return < div data-testid = "value" > { this . props . value } </ div >
333
375
}
334
376
}
335
377
336
- @connect ( ( state ) => ( { value : state . nestedB . value } ) )
337
- class ComponentB extends Component {
378
+ const WrapperComponentA = connect < PropsType , unknown , unknown , StateType > (
379
+ ( state ) => ( {
380
+ value : state . nestedA . value ,
381
+ } )
382
+ ) ( ComponentA )
383
+
384
+ class ComponentB extends Component < PropsType > {
338
385
render ( ) {
339
386
return < div data-testid = "value" > { this . props . value } </ div >
340
387
}
341
388
}
342
389
390
+ const WrapperComponentB = connect < PropsType , unknown , unknown , StateType > (
391
+ ( state ) => ( { value : state . nestedB . value } )
392
+ ) ( ComponentB )
393
+
343
394
const { getByTestId, rerender } = rtl . render (
344
395
< Provider store = { storeA } >
345
- < ComponentA />
396
+ < WrapperComponentA />
346
397
</ Provider >
347
398
)
348
399
349
400
expect ( getByTestId ( 'value' ) ) . toHaveTextContent ( 'expectedA' )
350
401
351
402
rerender (
352
403
< Provider store = { storeB } >
353
- < ComponentB />
404
+ < WrapperComponentB />
354
405
</ Provider >
355
406
)
356
407
0 commit comments