1
1
import React , { Component , ErrorInfo , useEffect } from "react"
2
- import { Observable , from , throwError } from "rxjs"
3
- import { delay , startWith } from "rxjs/operators"
2
+ import { Observable , throwError , concat , Subject } from "rxjs"
3
+ import { mergeMapTo , take , filter } from "rxjs/operators"
4
4
import { bind , Subscribe } from "@react-rxjs/core"
5
5
import { batchUpdates } from "./"
6
- import { render , screen } from "@testing-library/react"
6
+ import { act , render , screen } from "@testing-library/react"
7
7
8
8
const wait = ( ms : number ) => new Promise ( ( res ) => setTimeout ( res , ms ) )
9
9
10
- const [ useLatestNumber , latestNumber$ ] = bind ( ( id : string , batched : boolean ) =>
11
- ( id === "error"
12
- ? throwError ( "controlled error" )
13
- : from ( [ 1 , 2 , 3 , 4 , 5 ] )
14
- ) . pipe (
15
- delay ( 5 ) ,
16
- batched ? batchUpdates ( ) : ( x : Observable < number > ) => x ,
17
- startWith ( 0 ) ,
18
- ) ,
10
+ const next$ = new Subject < { batched : boolean ; error : boolean } > ( )
11
+ const [ useLatestNumber , latestNumber$ ] = bind (
12
+ ( batched : boolean , error : boolean ) => {
13
+ return concat (
14
+ [ 0 ] ,
15
+ next$ . pipe (
16
+ filter ( ( x ) => x . batched === batched && x . error === error ) ,
17
+ take ( 1 ) ,
18
+ mergeMapTo ( error ? throwError ( "controlled error" ) : [ 1 , 2 , 3 , 4 , 5 ] ) ,
19
+ batched ? batchUpdates ( ) : ( x : Observable < number > ) => x ,
20
+ ) ,
21
+ )
22
+ } ,
19
23
)
20
24
21
25
class TestErrorBoundary extends Component <
@@ -49,17 +53,17 @@ class TestErrorBoundary extends Component<
49
53
50
54
interface Props {
51
55
onRender : ( ) => void
52
- batched : boolean
53
- id : string
56
+ batched ? : boolean
57
+ error ?: boolean
54
58
}
55
- const Grandson : React . FC < Props > = ( { onRender, batched, id } ) => {
56
- const latestNumber = useLatestNumber ( id , batched )
59
+ const Grandson : React . FC < Props > = ( { onRender, batched, error } ) => {
60
+ const latestNumber = useLatestNumber ( ! ! batched , ! ! error )
57
61
useEffect ( onRender )
58
62
return < div > Grandson { latestNumber } </ div >
59
63
}
60
64
61
65
const Son : React . FC < Props > = ( props ) => {
62
- const latestNumber = useLatestNumber ( props . id , props . batched )
66
+ const latestNumber = useLatestNumber ( ! ! props . batched , ! ! props . error )
63
67
useEffect ( props . onRender )
64
68
return (
65
69
< div >
@@ -70,7 +74,7 @@ const Son: React.FC<Props> = (props) => {
70
74
}
71
75
72
76
const Father : React . FC < Props > = ( props ) => {
73
- const latestNumber = useLatestNumber ( props . id , props . batched )
77
+ const latestNumber = useLatestNumber ( ! ! props . batched , ! ! props . error )
74
78
useEffect ( props . onRender )
75
79
return (
76
80
< div >
@@ -98,16 +102,19 @@ describe("batchUpdates", () => {
98
102
test ( "it triggers nested updates when batchUpdates is not used" , async ( ) => {
99
103
const mockFn = jest . fn ( )
100
104
render (
101
- < Subscribe source$ = { latestNumber$ ( "noBatching" , false ) } >
102
- < Father id = "noBatching" batched = { false } onRender = { mockFn } />
105
+ < Subscribe source$ = { latestNumber$ ( false , false ) } >
106
+ < Father onRender = { mockFn } />
103
107
</ Subscribe > ,
104
108
)
105
109
expect ( screen . queryByText ( "Father 0" ) ) . not . toBeNull ( )
106
110
expect ( screen . queryByText ( "Son 0" ) ) . not . toBeNull ( )
107
111
expect ( screen . queryByText ( "Grandson 0" ) ) . not . toBeNull ( )
108
112
expect ( mockFn ) . toHaveBeenCalledTimes ( 3 )
109
113
110
- await wait ( 10 )
114
+ await act ( async ( ) => {
115
+ await wait ( 100 )
116
+ next$ . next ( { batched : false , error : false } )
117
+ } )
111
118
112
119
expect ( screen . queryByText ( "Father 5" ) ) . not . toBeNull ( )
113
120
expect ( screen . queryByText ( "Son 5" ) ) . not . toBeNull ( )
@@ -118,8 +125,8 @@ describe("batchUpdates", () => {
118
125
test ( "batchUpdates prevents unnecessary updates" , async ( ) => {
119
126
const mockFn = jest . fn ( )
120
127
render (
121
- < Subscribe source$ = { latestNumber$ ( "batchingAndComplete" , true ) } >
122
- < Father id = "batchingAndComplete" batched onRender = { mockFn } />
128
+ < Subscribe source$ = { latestNumber$ ( true , false ) } >
129
+ < Father batched onRender = { mockFn } />
123
130
</ Subscribe > ,
124
131
)
125
132
@@ -128,7 +135,10 @@ describe("batchUpdates", () => {
128
135
expect ( screen . queryByText ( "Grandson 0" ) ) . not . toBeNull ( )
129
136
expect ( mockFn ) . toHaveBeenCalledTimes ( 3 )
130
137
131
- await wait ( 10 )
138
+ await act ( async ( ) => {
139
+ await wait ( 100 )
140
+ next$ . next ( { batched : true , error : false } )
141
+ } )
132
142
133
143
expect ( screen . queryByText ( "Father 5" ) ) . not . toBeNull ( )
134
144
expect ( screen . queryByText ( "Son 5" ) ) . not . toBeNull ( )
@@ -141,20 +151,22 @@ describe("batchUpdates", () => {
141
151
const errorCallback = jest . fn ( )
142
152
render (
143
153
< TestErrorBoundary onError = { errorCallback } >
144
- < Father id = "error" batched = { true } onRender = { mockFn } />
154
+ < Father batched error onRender = { mockFn } />
145
155
</ TestErrorBoundary > ,
146
156
)
147
157
expect ( screen . queryByText ( "Father 0" ) ) . not . toBeNull ( )
148
158
expect ( screen . queryByText ( "Son 0" ) ) . not . toBeNull ( )
149
159
expect ( screen . queryByText ( "Grandson 0" ) ) . not . toBeNull ( )
150
160
expect ( mockFn ) . toHaveBeenCalledTimes ( 3 )
151
161
152
- await wait ( 10 )
162
+ await act ( async ( ) => {
163
+ next$ . next ( { batched : true , error : true } )
164
+ } )
153
165
154
- expect ( mockFn ) . toHaveBeenCalledTimes ( 3 )
155
166
expect ( errorCallback ) . toHaveBeenCalledWith (
156
167
"controlled error" ,
157
168
expect . any ( Object ) ,
158
169
)
170
+ expect ( mockFn ) . toHaveBeenCalledTimes ( 3 )
159
171
} )
160
172
} )
0 commit comments