@@ -5,39 +5,34 @@ import PropTypes from 'prop-types'
5
5
import semver from 'semver'
6
6
import { createStore } from 'redux'
7
7
import { Provider , createProvider , connect } from '../../src/index.js'
8
+ import { ReactReduxContext } from "../../src/components/context"
8
9
import * as rtl from 'react-testing-library'
9
10
import 'jest-dom/extend-expect'
11
+ import ReactDOM from "react-dom"
10
12
11
13
const createExampleTextReducer = ( ) => ( state = "example text" ) => state ;
12
14
13
15
describe ( 'React' , ( ) => {
14
16
describe ( 'Provider' , ( ) => {
15
17
afterEach ( ( ) => rtl . cleanup ( ) )
18
+
16
19
const createChild = ( storeKey = 'store' ) => {
17
20
class Child extends Component {
18
21
render ( ) {
19
- const store = this . context [ storeKey ] ;
20
-
21
- let text = '' ;
22
-
23
- if ( store ) {
24
- text = store . getState ( ) . toString ( )
25
- }
26
-
27
22
return (
28
23
< div data-testid = "store" >
29
- { storeKey } - { text }
24
+ < ReactReduxContext . Consumer >
25
+ { ( { storeState} ) => {
26
+ return `${ storeKey } - ${ storeState } `
27
+ } }
28
+ </ ReactReduxContext . Consumer >
29
+
30
30
</ div >
31
31
)
32
32
}
33
33
}
34
34
35
-
36
- Child . contextTypes = {
37
- [ storeKey ] : PropTypes . object . isRequired
38
- }
39
-
40
- return Child
35
+ return Child
41
36
}
42
37
const Child = createChild ( ) ;
43
38
@@ -90,10 +85,12 @@ describe('React', () => {
90
85
}
91
86
} )
92
87
93
- it ( 'should add the store to the child context' , ( ) => {
88
+ it ( 'should add the store state to context' , ( ) => {
94
89
const store = createStore ( createExampleTextReducer ( ) )
95
90
96
- const spy = jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } )
91
+ const spy = jest . spyOn ( console , 'error' ) . mockImplementation ( ( e ) => {
92
+ const q = 42 ;
93
+ } )
97
94
const tester = rtl . render (
98
95
< Provider store = { store } >
99
96
< Child />
@@ -105,22 +102,6 @@ describe('React', () => {
105
102
expect ( tester . getByTestId ( 'store' ) ) . toHaveTextContent ( 'store - example text' )
106
103
} )
107
104
108
- it ( 'should add the store to the child context using a custom store key' , ( ) => {
109
- const store = createStore ( createExampleTextReducer ( ) )
110
- const CustomProvider = createProvider ( 'customStoreKey' ) ;
111
- const CustomChild = createChild ( 'customStoreKey' ) ;
112
-
113
- const spy = jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } ) ;
114
- const tester = rtl . render (
115
- < CustomProvider store = { store } >
116
- < CustomChild />
117
- </ CustomProvider >
118
- )
119
- expect ( spy ) . toHaveBeenCalledTimes ( 0 )
120
- spy . mockRestore ( )
121
-
122
- expect ( tester . getByTestId ( 'store' ) ) . toHaveTextContent ( 'customStoreKey - example text' )
123
- } )
124
105
125
106
it ( 'should warn once when receiving a new store in props' , ( ) => {
126
107
const store1 = createStore ( ( state = 10 ) => state + 1 )
@@ -190,87 +171,118 @@ describe('React', () => {
190
171
innerStore . dispatch ( { type : 'INC' } )
191
172
expect ( innerMapStateToProps ) . toHaveBeenCalledTimes ( 2 )
192
173
} )
193
- } )
194
174
195
- it ( 'should pass state consistently to mapState' , ( ) => {
196
- function stringBuilder ( prev = '' , action ) {
197
- return action . type === 'APPEND'
198
- ? prev + action . body
199
- : prev
200
- }
201
175
202
- const store = createStore ( stringBuilder )
176
+ it ( 'should pass state consistently to mapState' , ( ) => {
177
+ function stringBuilder ( prev = '' , action ) {
178
+ return action . type === 'APPEND'
179
+ ? prev + action . body
180
+ : prev
181
+ }
182
+
183
+ const store = createStore ( stringBuilder )
203
184
204
- store . dispatch ( { type : 'APPEND' , body : 'a' } )
205
- let childMapStateInvokes = 0
185
+ store . dispatch ( { type : 'APPEND' , body : 'a' } )
186
+ let childMapStateInvokes = 0
206
187
207
- @connect ( state => ( { state } ) , null , null , { withRef : true } )
208
- class Container extends Component {
209
- emitChange ( ) {
210
- store . dispatch ( { type : 'APPEND' , body : 'b' } )
211
- }
188
+ @connect ( state => ( { state } ) , null , null , { withRef : true } )
189
+ class Container extends Component {
190
+ emitChange ( ) {
191
+ store . dispatch ( { type : 'APPEND' , body : 'b' } )
192
+ }
212
193
213
- render ( ) {
214
- return (
215
- < div >
216
- < button onClick = { this . emitChange . bind ( this ) } > change</ button >
217
- < ChildContainer parentState = { this . props . state } />
218
- </ div >
219
- )
194
+ render ( ) {
195
+ return (
196
+ < div >
197
+ < button onClick = { this . emitChange . bind ( this ) } > change</ button >
198
+ < ChildContainer parentState = { this . props . state } />
199
+ </ div >
200
+ )
201
+ }
220
202
}
221
- }
222
203
223
- @connect ( ( state , parentProps ) => {
224
- childMapStateInvokes ++
225
- // The state from parent props should always be consistent with the current state
226
- expect ( state ) . toEqual ( parentProps . parentState )
227
- return { }
228
- } )
229
- class ChildContainer extends Component {
230
- render ( ) {
231
- return < div />
204
+ @connect ( ( state , parentProps ) => {
205
+ childMapStateInvokes ++
206
+ // The state from parent props should always be consistent with the current state
207
+ expect ( state ) . toEqual ( parentProps . parentState )
208
+ return { }
209
+ } )
210
+ class ChildContainer extends Component {
211
+ render ( ) {
212
+ return < div />
213
+ }
232
214
}
233
- }
234
215
235
- const tester = rtl . render (
236
- < Provider store = { store } >
237
- < Container />
238
- </ Provider >
239
- )
216
+ const tester = rtl . render (
217
+ < Provider store = { store } >
218
+ < Container />
219
+ </ Provider >
220
+ )
221
+
222
+ expect ( childMapStateInvokes ) . toBe ( 1 )
240
223
241
- expect ( childMapStateInvokes ) . toBe ( 1 )
224
+ // The store state stays consistent when setState calls are batched
225
+ store . dispatch ( { type : 'APPEND' , body : 'c' } )
226
+ expect ( childMapStateInvokes ) . toBe ( 2 )
242
227
243
- // The store state stays consistent when setState calls are batched
244
- store . dispatch ( { type : 'APPEND' , body : 'c' } )
245
- expect ( childMapStateInvokes ) . toBe ( 2 )
228
+ // setState calls DOM handlers are batched
229
+ const button = tester . getByText ( 'change' )
230
+ rtl . fireEvent . click ( button )
231
+ expect ( childMapStateInvokes ) . toBe ( 3 )
246
232
247
- // setState calls DOM handlers are batched
248
- const button = tester . getByText ( 'change' )
249
- rtl . fireEvent . click ( button )
250
- expect ( childMapStateInvokes ) . toBe ( 3 )
233
+ // Provider uses unstable_batchedUpdates() under the hood
234
+ store . dispatch ( { type : 'APPEND' , body : 'd' } )
235
+ expect ( childMapStateInvokes ) . toBe ( 4 )
236
+ } )
251
237
252
- // Provider uses unstable_batchedUpdates() under the hood
253
- store . dispatch ( { type : 'APPEND' , body : 'd' } )
254
- expect ( childMapStateInvokes ) . toBe ( 4 )
255
- } )
256
238
239
+ it . skip ( 'works in <StrictMode> without warnings (React 16.3+)' , ( ) => {
240
+ if ( ! React . StrictMode ) {
241
+ return
242
+ }
243
+ const spy = jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } )
244
+ const store = createStore ( ( ) => ( { } ) )
257
245
258
- it . skip ( 'works in <StrictMode> without warnings (React 16.3+)' , ( ) => {
259
- if ( ! React . StrictMode ) {
260
- return
261
- }
262
- const spy = jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } )
263
- const store = createStore ( ( ) => ( { } ) )
246
+ rtl . render (
247
+ < React . StrictMode >
248
+ < Provider store = { store } >
249
+ < div />
250
+ </ Provider >
251
+ </ React . StrictMode >
252
+ )
264
253
265
- rtl . render (
266
- < React . StrictMode >
254
+ expect ( spy ) . not . toHaveBeenCalled ( )
255
+ } )
256
+
257
+
258
+ it ( 'should unsubscribe before unmounting' , ( ) => {
259
+ const store = createStore ( createExampleTextReducer ( ) )
260
+ const subscribe = store . subscribe
261
+
262
+ // Keep track of unsubscribe by wrapping subscribe()
263
+ const spy = jest . fn ( ( ) => ( { } ) )
264
+ store . subscribe = ( listener ) => {
265
+ const unsubscribe = subscribe ( listener )
266
+ return ( ) => {
267
+ spy ( )
268
+ return unsubscribe ( )
269
+ }
270
+ }
271
+
272
+ const div = document . createElement ( 'div' )
273
+ ReactDOM . render (
267
274
< Provider store = { store } >
268
275
< div />
269
- </ Provider >
270
- </ React . StrictMode >
271
- )
276
+ </ Provider > ,
277
+ div
278
+ )
272
279
273
- expect ( spy ) . not . toHaveBeenCalled ( )
280
+ expect ( spy ) . toHaveBeenCalledTimes ( 0 )
281
+ ReactDOM . unmountComponentAtNode ( div )
282
+ expect ( spy ) . toHaveBeenCalledTimes ( 1 )
283
+ } )
274
284
} )
275
285
286
+
287
+
276
288
} )
0 commit comments