@@ -7,7 +7,12 @@ import {
7
7
} from "../../rendering/functionalComponents" ;
8
8
import * as rendering from "../../rendering/render" ;
9
9
10
- import { computed , createEffect , createSignal } from "../../signals/signal" ;
10
+ import {
11
+ computed ,
12
+ createEffect ,
13
+ createRef ,
14
+ createSignal ,
15
+ } from "../../signals/signal" ;
11
16
12
17
vi . stubGlobal ( "requestIdleCallback" , ( cb ) => {
13
18
queueMicrotask ( ( ) => cb ( { timeRemaining : ( ) => 2 } ) ) ;
@@ -98,3 +103,196 @@ describe("Functional Components life cycle", () => {
98
103
expect ( computedCount ) . toBe ( 2 ) ;
99
104
} ) ;
100
105
} ) ;
106
+
107
+ describe ( "hooks lifecycle" , ( ) => {
108
+ it ( "create effect should run synchrounously when outside of a FC" , async ( ) => {
109
+ let count = 0 ;
110
+ createEffect ( ( ) => {
111
+ count ++ ;
112
+ } ) ;
113
+ await Promise . resolve ( ) ;
114
+ expect ( count ) . toBe ( 1 ) ;
115
+ } ) ;
116
+ it ( "computed should run synchrounously when outside of a FC or inside and should run only once" , async ( ) => {
117
+ let fn = vi . fn ( ) ;
118
+
119
+ const signal = createSignal ( 1 ) ;
120
+ const comp = computed ( ( ) => {
121
+ fn ( ) ;
122
+ return signal . value * 2 ;
123
+ } ) ;
124
+ expect ( fn ) . toHaveBeenCalledTimes ( 1 ) ;
125
+ expect ( comp . value ) . toBe ( 2 ) ;
126
+
127
+ // inside FC
128
+ const FC = ( ) => {
129
+ const signal = createSignal ( 1 ) ;
130
+ const comp = computed ( ( ) => {
131
+ fn ( ) ;
132
+ return signal . value * 2 ;
133
+ } ) ;
134
+ return < div > </ div > ;
135
+ } ;
136
+
137
+ const fiber = (
138
+ < div >
139
+ < FC />
140
+ </ div >
141
+ ) ;
142
+
143
+ createFiber ( fiber ) ;
144
+ commitFiber ( fiber ) ;
145
+
146
+ expect ( fn ) . toHaveBeenCalledTimes ( 2 ) ;
147
+ await Promise . resolve ( ) ;
148
+ expect ( fn ) . toHaveBeenCalledTimes ( 2 ) ;
149
+ } ) ;
150
+
151
+ it ( "create effect should run asynchronously when inside of a FC, in render and commitFiber both" , async ( ) => {
152
+ let fn1 = vi . fn ( ) ;
153
+ let fn2 = vi . fn ( ) ;
154
+ // render
155
+ const FC = ( ) => {
156
+ createEffect ( ( ) => {
157
+ fn1 ( ) ;
158
+ } ) ;
159
+ fn2 ( ) ;
160
+ return < div > </ div > ;
161
+ } ;
162
+
163
+ const container = document . createElement ( "div" ) ;
164
+
165
+ rendering . render ( < FC /> , container ) ;
166
+ await Promise . resolve ( ) ;
167
+
168
+ expect ( container . innerHTML ) . toBe ( "<div></div>" ) ;
169
+ expect ( fn1 ) . toHaveBeenCalledAfter ( fn2 ) ;
170
+
171
+ // commitFiber
172
+ const fiber = (
173
+ < div >
174
+ < FC />
175
+ </ div >
176
+ ) ;
177
+
178
+ createFiber ( fiber ) ;
179
+ commitFiber ( fiber ) ;
180
+
181
+ expect ( container . innerHTML ) . toBe ( "<div></div>" ) ;
182
+ await Promise . resolve ( ) ;
183
+
184
+ expect ( fn1 ) . toHaveBeenCalledTimes ( 2 ) ;
185
+ expect ( fn2 ) . toHaveBeenCalledTimes ( 2 ) ;
186
+ expect ( fn1 ) . toHaveBeenCalledAfter ( fn2 ) ;
187
+ } ) ;
188
+
189
+ // this is just a test to make sure that this does not cause an infinite loop and it is an anti-pattern
190
+ it ( "create effect should run twice not infinitely when inside a FC with self dependency" , async ( ) => {
191
+ let fn1 = vi . fn ( ) ;
192
+ let fn2 = vi . fn ( ) ;
193
+ // render
194
+ const FC = ( ) => {
195
+ const signal = createSignal < number > ( 1 ) ;
196
+ createEffect ( ( ) => {
197
+ signal . value ;
198
+ // if the signal is registered first then only it will be called twice
199
+ signal . update ( ( prev ) => prev + 1 ) ;
200
+ fn1 ( ) ;
201
+ } ) ;
202
+ fn2 ( ) ;
203
+ return < div > </ div > ;
204
+ } ;
205
+
206
+ const fiber = (
207
+ < div >
208
+ < FC />
209
+ </ div >
210
+ ) ;
211
+
212
+ createFiber ( fiber ) ;
213
+ commitFiber ( fiber ) ;
214
+
215
+ await Promise . resolve ( ) ;
216
+
217
+ expect ( fn1 ) . toHaveBeenCalledTimes ( 1 ) ;
218
+ await Promise . resolve ( ) ;
219
+
220
+ // if the signal is registered first then only it will be called twice
221
+ expect ( fn1 ) . toHaveBeenCalledTimes ( 2 ) ;
222
+ } ) ;
223
+
224
+ it ( "createEffect with a ref should always have the ref defined" , async ( ) => {
225
+ // render
226
+
227
+ const fn = vi . fn ( ) ;
228
+ const FC = ( ) => {
229
+ const ref = createRef ( ) ;
230
+
231
+ createEffect ( ( ) => {
232
+ fn ( ref . current ) ;
233
+ } ) ;
234
+
235
+ return (
236
+ < div >
237
+ < p > Child1</ p >
238
+ < div >
239
+ Child2
240
+ < p > Subchild 2</ p >
241
+ < div >
242
+ Subchild 3< p ref = { ref } > Subchild 4</ p >
243
+ </ div >
244
+ </ div >
245
+ </ div >
246
+ ) ;
247
+ } ;
248
+
249
+ const fiber = (
250
+ < div >
251
+ < FC />
252
+ </ div >
253
+ ) ;
254
+
255
+ createFiber ( fiber ) ;
256
+ commitFiber ( fiber ) ;
257
+
258
+ expect ( fn ) . toHaveBeenCalledTimes ( 0 ) ;
259
+ await Promise . resolve ( ) ;
260
+
261
+ expect ( fn ) . toHaveBeenCalledWith ( expect . any ( HTMLParagraphElement ) ) ;
262
+ } ) ;
263
+ it ( "createEffect with a ref should always have the ref defined, with render also" , async ( ) => {
264
+ // render
265
+
266
+ const fn = vi . fn ( ) ;
267
+ const FC = ( ) => {
268
+ const ref = createRef ( ) ;
269
+
270
+ createEffect ( ( ) => {
271
+ fn ( ref . current ) ;
272
+ } ) ;
273
+
274
+ return (
275
+ < div >
276
+ < p > Child1</ p >
277
+ < div >
278
+ Child2
279
+ < p > Subchild 2</ p >
280
+ < div >
281
+ Subchild 3< p ref = { ref } > Subchild 4</ p >
282
+ </ div >
283
+ </ div >
284
+ </ div >
285
+ ) ;
286
+ } ;
287
+
288
+ rendering . render ( < FC /> , document . body ) ;
289
+
290
+ expect ( document . body . innerHTML ) . toBe ( "" ) ;
291
+ expect ( fn ) . toHaveBeenCalledTimes ( 0 ) ;
292
+ await Promise . resolve ( ) ;
293
+ expect ( document . body . innerHTML ) . toBe (
294
+ "<div><p>Child1</p><div>Child2<p>Subchild 2</p><div>Subchild 3<p>Subchild 4</p></div></div></div>"
295
+ ) ;
296
+ expect ( fn ) . toHaveBeenCalledWith ( expect . any ( HTMLParagraphElement ) ) ;
297
+ } ) ;
298
+ } ) ;
0 commit comments