@@ -234,35 +234,91 @@ it('returns the defaultValue, when provided, if any facet has NO_VALUE and skip
234
234
expect ( callback ) . not . toHaveBeenCalledWith ( )
235
235
} )
236
236
237
- it ( 'should always have the current value of tracked facets' , ( ) => {
238
- const facetA = createFacet < string > ( { initialValue : NO_VALUE } )
237
+ describe ( 'regressions' , ( ) => {
238
+ it ( 'should always have the current value of tracked facets' , ( ) => {
239
+ const facetA = createFacet < string > ( { initialValue : NO_VALUE } )
240
+
241
+ let handler : ( event : string ) => void = ( ) => { }
242
+
243
+ const TestComponent = ( ) => {
244
+ handler = useFacetCallback (
245
+ ( a ) => ( b : string ) => {
246
+ return a + b
247
+ } ,
248
+ [ ] ,
249
+ [ facetA ] ,
250
+ )
251
+
252
+ return null
253
+ }
239
254
240
- let handler : ( event : string ) => void = ( ) => { }
255
+ // We make sure to be the first listener registered, so this is called before
256
+ // the listener within the useFacetCallback (which would have created the issue)
257
+ facetA . observe ( ( ) => {
258
+ const result = handler ( 'string' )
259
+ expect ( result ) . toBe ( 'newstring' )
260
+ } )
241
261
242
- const TestComponent = ( ) => {
243
- handler = useFacetCallback (
244
- ( a ) => ( b : string ) => {
245
- return a + b
262
+ render ( < TestComponent /> )
263
+
264
+ // In this act, the effect within useFacetCallback will be executed, subscribing for changes of the facetA
265
+ // Then we set the value, causing the listener above to being called
266
+ act ( ( ) => {
267
+ facetA . set ( 'new' )
268
+ } )
269
+ } )
270
+
271
+ it ( 'should always have the current value of tracked facets (even after another component unmounts)' , ( ) => {
272
+ const facetA = createFacet < string > ( {
273
+ initialValue : NO_VALUE ,
274
+
275
+ // We need to have a value from a startSubscription so that after the last listener is removed, we set the facet back to NO_VALUE
276
+ startSubscription : ( update ) => {
277
+ update ( 'value' )
278
+ return ( ) => { }
246
279
} ,
247
- [ ] ,
248
- [ facetA ] ,
249
- )
280
+ } )
250
281
251
- return null
252
- }
282
+ let handler : ( event : string ) => void = ( ) => { }
253
283
254
- // We make sure to be the first listener registered, so this is called before
255
- // the listener within the useFacetCallback (which would have created the issue)
256
- facetA . observe ( ( ) => {
257
- const result = handler ( 'string' )
258
- expect ( result ) . toBe ( 'newstring' )
259
- } )
284
+ const TestComponentA = ( ) => {
285
+ handler = useFacetCallback (
286
+ ( a ) => ( b : string ) => {
287
+ return a + b
288
+ } ,
289
+ [ ] ,
290
+ [ facetA ] ,
291
+ )
260
292
261
- render ( < TestComponent /> )
293
+ return null
294
+ }
262
295
263
- // In this act, the effect within useFacetCallback will be executed, subscribing for changes of the facetA
264
- // Then we set the value, causing the listener above to being called
265
- act ( ( ) => {
266
- facetA . set ( 'new' )
296
+ const TestComponentB = ( ) => {
297
+ useFacetCallback ( ( ) => ( ) => { } , [ ] , [ facetA ] )
298
+
299
+ return null
300
+ }
301
+
302
+ // We mount both components, both internally calling the useFacetCallback to start subscriptions towards the facetA
303
+ const { rerender } = render (
304
+ < >
305
+ < TestComponentA />
306
+ < TestComponentB />
307
+ </ > ,
308
+ )
309
+
310
+ // Then we unmount one of the components, causing it to unsubscribe from the facetA
311
+ rerender (
312
+ < >
313
+ < TestComponentA />
314
+ </ > ,
315
+ )
316
+
317
+ // However, with a prior implementation, a shared instance of a listener (noop) was used across all useFacetCallback usages
318
+ // causing a mismatch between calls to observer and unsubscribe.
319
+ act ( ( ) => {
320
+ const result = handler ( 'string' )
321
+ expect ( result ) . toBe ( 'valuestring' )
322
+ } )
267
323
} )
268
324
} )
0 commit comments