@wordpress/data 8.6.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@ import { act, render, fireEvent, screen } from '@testing-library/react';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { Component, useState, useReducer } from '@wordpress/element';
9
+ import { useLayoutEffect, useState, useReducer } from '@wordpress/element';
10
10
 
11
11
  /**
12
12
  * Internal dependencies
@@ -19,6 +19,19 @@ import {
19
19
  } from '../../..';
20
20
  import useSelect from '..';
21
21
 
22
+ function counterStore( initialCount = 0, step = 1 ) {
23
+ return {
24
+ reducer: ( state = initialCount, action ) =>
25
+ action.type === 'INC' ? state + step : state,
26
+ actions: {
27
+ inc: () => ( { type: 'INC' } ),
28
+ },
29
+ selectors: {
30
+ get: ( state ) => state,
31
+ },
32
+ };
33
+ }
34
+
22
35
  describe( 'useSelect', () => {
23
36
  let registry;
24
37
  beforeEach( () => {
@@ -109,7 +122,7 @@ describe( 'useSelect', () => {
109
122
  );
110
123
 
111
124
  expect( selectSpyFoo ).toHaveBeenCalledTimes( 2 );
112
- expect( selectSpyBar ).toHaveBeenCalledTimes( 2 );
125
+ expect( selectSpyBar ).toHaveBeenCalledTimes( 1 );
113
126
  expect( TestComponent ).toHaveBeenCalledTimes( 3 );
114
127
 
115
128
  // Ensure expected state was rendered.
@@ -180,6 +193,81 @@ describe( 'useSelect', () => {
180
193
  expect( Child ).toHaveBeenCalledTimes( 1 );
181
194
  } );
182
195
 
196
+ it( 'incrementally subscribes to newly selected stores', () => {
197
+ registry.registerStore( 'store-main', counterStore() );
198
+ registry.registerStore( 'store-even', counterStore( 0, 2 ) );
199
+ registry.registerStore( 'store-odd', counterStore( 1, 2 ) );
200
+
201
+ const mapSelect = jest.fn( ( select ) => {
202
+ const first = select( 'store-main' ).get();
203
+ // select from other stores depending on whether main value is even or odd
204
+ const secondStore = first % 2 === 1 ? 'store-odd' : 'store-even';
205
+ const second = select( secondStore ).get();
206
+ return first + ':' + second;
207
+ } );
208
+
209
+ const TestComponent = jest.fn( () => {
210
+ const data = useSelect( mapSelect, [] );
211
+ return <div role="status">{ data }</div>;
212
+ } );
213
+
214
+ render(
215
+ <RegistryProvider value={ registry }>
216
+ <TestComponent />
217
+ </RegistryProvider>
218
+ );
219
+
220
+ expect( mapSelect ).toHaveBeenCalledTimes( 2 );
221
+ expect( TestComponent ).toHaveBeenCalledTimes( 1 );
222
+ expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0:0' );
223
+
224
+ // check that increment in store-even triggers a render
225
+ act( () => {
226
+ registry.dispatch( 'store-even' ).inc();
227
+ } );
228
+
229
+ expect( mapSelect ).toHaveBeenCalledTimes( 3 );
230
+ expect( TestComponent ).toHaveBeenCalledTimes( 2 );
231
+ expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0:2' );
232
+
233
+ // check that increment in store-odd doesn't trigger a render (not listening yet)
234
+ act( () => {
235
+ registry.dispatch( 'store-odd' ).inc();
236
+ } );
237
+
238
+ expect( mapSelect ).toHaveBeenCalledTimes( 3 );
239
+ expect( TestComponent ).toHaveBeenCalledTimes( 2 );
240
+ expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0:2' );
241
+
242
+ // check that increment in main store switches to store-odd
243
+ act( () => {
244
+ registry.dispatch( 'store-main' ).inc();
245
+ } );
246
+
247
+ expect( mapSelect ).toHaveBeenCalledTimes( 4 );
248
+ expect( TestComponent ).toHaveBeenCalledTimes( 3 );
249
+ expect( screen.getByRole( 'status' ) ).toHaveTextContent( '1:3' );
250
+
251
+ // check that increment in store-odd triggers a render
252
+ act( () => {
253
+ registry.dispatch( 'store-odd' ).inc();
254
+ } );
255
+
256
+ expect( mapSelect ).toHaveBeenCalledTimes( 5 );
257
+ expect( TestComponent ).toHaveBeenCalledTimes( 4 );
258
+ expect( screen.getByRole( 'status' ) ).toHaveTextContent( '1:5' );
259
+
260
+ // check that increment in store-even triggers a mapSelect call (still listening)
261
+ // but not a render (not used for selected value which doesn't change)
262
+ act( () => {
263
+ registry.dispatch( 'store-even' ).inc();
264
+ } );
265
+
266
+ expect( mapSelect ).toHaveBeenCalledTimes( 6 );
267
+ expect( TestComponent ).toHaveBeenCalledTimes( 4 );
268
+ expect( screen.getByRole( 'status' ) ).toHaveTextContent( '1:5' );
269
+ } );
270
+
183
271
  describe( 'rerenders as expected with various mapSelect return types', () => {
184
272
  const getComponent = ( mapSelectSpy ) => () => {
185
273
  const data = useSelect( mapSelectSpy, [] );
@@ -254,40 +342,20 @@ describe( 'useSelect', () => {
254
342
  } );
255
343
 
256
344
  describe( 're-calls the selector as few times as possible', () => {
257
- const counterStore = {
258
- actions: {
259
- increment: () => ( { type: 'INCREMENT' } ),
260
- },
261
- reducer: ( state, action ) => {
262
- if ( ! state ) {
263
- return { counter: 0 };
264
- }
265
- if ( action?.type === 'INCREMENT' ) {
266
- return { counter: state.counter + 1 };
267
- }
268
- return state;
269
- },
270
- selectors: {
271
- getCounter: ( state ) => state.counter,
272
- },
273
- };
274
-
275
345
  it( 'only calls the selectors it has selected', () => {
276
- registry.registerStore( 'store-1', counterStore );
277
- registry.registerStore( 'store-2', counterStore );
346
+ registry.registerStore( 'store-1', counterStore() );
347
+ registry.registerStore( 'store-2', counterStore() );
278
348
 
279
349
  const selectCount1 = jest.fn();
280
350
  const selectCount2 = jest.fn();
281
351
 
282
352
  const TestComponent = jest.fn( () => {
283
353
  const count1 = useSelect(
284
- ( select ) =>
285
- selectCount1() || select( 'store-1' ).getCounter(),
354
+ ( select ) => selectCount1() || select( 'store-1' ).get(),
286
355
  []
287
356
  );
288
357
  useSelect(
289
- ( select ) =>
290
- selectCount2() || select( 'store-2' ).getCounter(),
358
+ ( select ) => selectCount2() || select( 'store-2' ).get(),
291
359
  []
292
360
  );
293
361
 
@@ -306,7 +374,7 @@ describe( 'useSelect', () => {
306
374
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0' );
307
375
 
308
376
  act( () => {
309
- registry.dispatch( 'store-2' ).increment();
377
+ registry.dispatch( 'store-2' ).inc();
310
378
  } );
311
379
 
312
380
  expect( selectCount1 ).toHaveBeenCalledTimes( 2 );
@@ -315,7 +383,7 @@ describe( 'useSelect', () => {
315
383
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0' );
316
384
 
317
385
  act( () => {
318
- registry.dispatch( 'store-1' ).increment();
386
+ registry.dispatch( 'store-1' ).inc();
319
387
  } );
320
388
 
321
389
  expect( selectCount1 ).toHaveBeenCalledTimes( 3 );
@@ -328,9 +396,9 @@ describe( 'useSelect', () => {
328
396
  } );
329
397
 
330
398
  it( 'can subscribe to multiple stores at once', () => {
331
- registry.registerStore( 'store-1', counterStore );
332
- registry.registerStore( 'store-2', counterStore );
333
- registry.registerStore( 'store-3', counterStore );
399
+ registry.registerStore( 'store-1', counterStore() );
400
+ registry.registerStore( 'store-2', counterStore() );
401
+ registry.registerStore( 'store-3', counterStore() );
334
402
 
335
403
  const selectCount1And2 = jest.fn();
336
404
 
@@ -338,8 +406,8 @@ describe( 'useSelect', () => {
338
406
  const { count1, count2 } = useSelect(
339
407
  ( select ) =>
340
408
  selectCount1And2() || {
341
- count1: select( 'store-1' ).getCounter(),
342
- count2: select( 'store-2' ).getCounter(),
409
+ count1: select( 'store-1' ).get(),
410
+ count2: select( 'store-2' ).get(),
343
411
  },
344
412
  []
345
413
  );
@@ -361,14 +429,14 @@ describe( 'useSelect', () => {
361
429
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0,0' );
362
430
 
363
431
  act( () => {
364
- registry.dispatch( 'store-2' ).increment();
432
+ registry.dispatch( 'store-2' ).inc();
365
433
  } );
366
434
 
367
435
  expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 );
368
436
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0,1' );
369
437
 
370
438
  act( () => {
371
- registry.dispatch( 'store-3' ).increment();
439
+ registry.dispatch( 'store-3' ).inc();
372
440
  } );
373
441
 
374
442
  expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 );
@@ -376,9 +444,9 @@ describe( 'useSelect', () => {
376
444
  } );
377
445
 
378
446
  it( 're-calls the selector when deps changed', () => {
379
- registry.registerStore( 'store-1', counterStore );
380
- registry.registerStore( 'store-2', counterStore );
381
- registry.registerStore( 'store-3', counterStore );
447
+ registry.registerStore( 'store-1', counterStore() );
448
+ registry.registerStore( 'store-2', counterStore() );
449
+ registry.registerStore( 'store-3', counterStore() );
382
450
 
383
451
  let dep, setDep;
384
452
  const selectCount1AndDep = jest.fn();
@@ -388,7 +456,7 @@ describe( 'useSelect', () => {
388
456
  const state = useSelect(
389
457
  ( select ) =>
390
458
  selectCount1AndDep() || {
391
- count1: select( 'store-1' ).getCounter(),
459
+ count1: select( 'store-1' ).get(),
392
460
  dep,
393
461
  },
394
462
  [ dep ]
@@ -416,63 +484,40 @@ describe( 'useSelect', () => {
416
484
  setDep( 1 );
417
485
  } );
418
486
 
419
- expect( selectCount1AndDep ).toHaveBeenCalledTimes( 4 );
487
+ expect( selectCount1AndDep ).toHaveBeenCalledTimes( 3 );
420
488
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
421
489
  'count:0,dep:1'
422
490
  );
423
491
 
424
492
  act( () => {
425
- registry.dispatch( 'store-1' ).increment();
493
+ registry.dispatch( 'store-1' ).inc();
426
494
  } );
427
495
 
428
- expect( selectCount1AndDep ).toHaveBeenCalledTimes( 5 );
496
+ expect( selectCount1AndDep ).toHaveBeenCalledTimes( 4 );
429
497
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
430
498
  'count:1,dep:1'
431
499
  );
432
500
  } );
433
501
 
434
- it( 'captures state changes scheduled between render and effect', () => {
435
- registry.registerStore( 'store-1', counterStore );
436
-
437
- class ChildComponent extends Component {
438
- componentDidUpdate( prevProps ) {
439
- if (
440
- this.props.childShouldDispatch &&
441
- this.props.childShouldDispatch !==
442
- prevProps.childShouldDispatch
443
- ) {
444
- registry.dispatch( 'store-1' ).increment();
445
- }
446
- }
447
-
448
- render() {
449
- return null;
450
- }
451
- }
502
+ it( 'captures state changes scheduled between render and subscription', () => {
503
+ registry.registerStore( 'store-1', counterStore() );
452
504
 
453
- const selectCount1AndDep = jest.fn( ( select ) => ( {
454
- count1: select( 'store-1' ).getCounter(),
505
+ const selectCount1 = jest.fn( ( select ) => ( {
506
+ count1: select( 'store-1' ).get(),
455
507
  } ) );
456
508
 
457
- const TestComponent = () => {
458
- const [ childShouldDispatch, setChildShouldDispatch ] =
459
- useState( false );
460
- const state = useSelect( selectCount1AndDep, [] );
509
+ const TestComponent = jest.fn( () => {
510
+ const { count1 } = useSelect( selectCount1, [] );
461
511
 
462
- return (
463
- <>
464
- <div role="status">count1:{ state.count1 }</div>
465
- <ChildComponent
466
- childShouldDispatch={ childShouldDispatch }
467
- />
468
- <button
469
- onClick={ () => setChildShouldDispatch( true ) }
470
- >
471
- triggerChildDispatch
472
- </button>
473
- </>
474
- );
475
- };
512
+ // Increment the store value from 0 to 1 after render and before subscription
513
+ useLayoutEffect( () => {
514
+ if ( count1 === 0 ) {
515
+ registry.dispatch( 'store-1' ).inc();
516
+ }
517
+ }, [ count1 ] );
518
+
519
+ return <div role="status">count1:{ count1 }</div>;
520
+ } );
476
521
 
477
522
  render(
478
523
  <RegistryProvider value={ registry }>
@@ -480,30 +525,93 @@ describe( 'useSelect', () => {
480
525
  </RegistryProvider>
481
526
  );
482
527
 
483
- fireEvent.click( screen.getByText( 'triggerChildDispatch' ) );
528
+ // One select on initial render, and one in `checkIfSnapshotChanged` after subscribing.
529
+ // There's a third selector call on the second render, but that one returns a memoized value.
530
+ expect( selectCount1 ).toHaveBeenCalledTimes( 2 );
484
531
 
485
- expect( selectCount1AndDep ).toHaveBeenCalledTimes( 3 );
532
+ // Initial render and second render after counter increment (which is expected to be detected).
533
+ expect( TestComponent ).toHaveBeenCalledTimes( 2 );
534
+
535
+ // Finally rendered with the incremented counter's value.
486
536
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
487
537
  'count1:1'
488
538
  );
489
539
  } );
490
540
 
541
+ it( 'captures state changes scheduled between render and effect after selector change', () => {
542
+ registry.registerStore( 'names', {
543
+ reducer: ( state = {}, action ) => {
544
+ if ( action.type === 'SET_NAME' ) {
545
+ return {
546
+ ...state,
547
+ [ action.id ]: action.name,
548
+ };
549
+ }
550
+ return state;
551
+ },
552
+ actions: {
553
+ setName: ( id, name ) => ( { type: 'SET_NAME', id, name } ),
554
+ },
555
+ selectors: {
556
+ getName: ( state, id ) => state[ id ] ?? 'null',
557
+ },
558
+ } );
559
+
560
+ const renderedItems = [];
561
+
562
+ function TestComponent() {
563
+ const [ blockId, setBlockId ] = useState( 1 );
564
+
565
+ const name = useSelect(
566
+ ( select ) => select( 'names' ).getName( blockId ),
567
+ [ blockId ]
568
+ );
569
+
570
+ // Change name of block 2. The store listener will still use the old selector
571
+ // for block 1, because a new one will be stored by an effect a moment later,
572
+ // but we're testing that it still won't miss the update, because one more check
573
+ // will happen in that effect.
574
+ useLayoutEffect( () => {
575
+ if ( blockId === 2 ) {
576
+ registry.dispatch( 'names' ).setName( 2, 'new2' );
577
+ }
578
+ }, [ blockId ] );
579
+
580
+ renderedItems.push( name );
581
+
582
+ return (
583
+ <button onClick={ () => setBlockId( 2 ) }>
584
+ change-block
585
+ </button>
586
+ );
587
+ }
588
+
589
+ render(
590
+ <RegistryProvider value={ registry }>
591
+ <TestComponent />
592
+ </RegistryProvider>
593
+ );
594
+ expect( renderedItems ).toEqual( [ 'null' ] );
595
+
596
+ fireEvent.click( screen.getByRole( 'button' ) );
597
+ // After click, there are two new renders:
598
+ // 1. With of block 2, after state update of `blockId` from 1 to 2
599
+ // 2. After dispatching an action to change 2's name to `new2`
600
+ expect( renderedItems ).toEqual( [ 'null', 'null', 'new2' ] );
601
+ } );
602
+
491
603
  it( 'handles registry selectors', () => {
492
604
  const getCount1And2 = createRegistrySelector(
493
605
  ( select ) => ( state ) => ( {
494
- count1: state.counter,
495
- count2: select( 'store-2' ).getCounter(),
606
+ count1: state,
607
+ count2: select( 'store-2' ).get(),
496
608
  } )
497
609
  );
498
610
 
499
- registry.registerStore( 'store-1', {
500
- ...counterStore,
501
- selectors: {
502
- ...counterStore.selectors,
503
- getCount1And2,
504
- },
505
- } );
506
- registry.registerStore( 'store-2', counterStore );
611
+ const store1Spec = counterStore();
612
+ Object.assign( store1Spec.selectors, { getCount1And2 } );
613
+ registry.registerStore( 'store-1', store1Spec );
614
+ registry.registerStore( 'store-2', counterStore() );
507
615
 
508
616
  const selectCount1And2 = jest.fn();
509
617
 
@@ -534,7 +642,7 @@ describe( 'useSelect', () => {
534
642
  );
535
643
 
536
644
  act( () => {
537
- registry.dispatch( 'store-2' ).increment();
645
+ registry.dispatch( 'store-2' ).inc();
538
646
  } );
539
647
 
540
648
  expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 );
@@ -544,8 +652,8 @@ describe( 'useSelect', () => {
544
652
  } );
545
653
 
546
654
  it( 'handles conditional statements in selectors', () => {
547
- registry.registerStore( 'store-1', counterStore );
548
- registry.registerStore( 'store-2', counterStore );
655
+ registry.registerStore( 'store-1', counterStore() );
656
+ registry.registerStore( 'store-2', counterStore() );
549
657
 
550
658
  const selectCount1 = jest.fn();
551
659
  const selectCount2 = jest.fn();
@@ -559,13 +667,11 @@ describe( 'useSelect', () => {
559
667
  ( select ) => {
560
668
  if ( shouldSelectCount1 ) {
561
669
  selectCount1();
562
- select( 'store-1' ).getCounter();
563
- return 'count1';
670
+ return 'count1:' + select( 'store-1' ).get();
564
671
  }
565
672
 
566
673
  selectCount2();
567
- select( 'store-2' ).getCounter();
568
- return 'count2';
674
+ return 'count2:' + select( 'store-2' ).get();
569
675
  },
570
676
  [ shouldSelectCount1 ]
571
677
  );
@@ -587,29 +693,40 @@ describe( 'useSelect', () => {
587
693
  expect( selectCount1 ).toHaveBeenCalledTimes( 0 );
588
694
  expect( selectCount2 ).toHaveBeenCalledTimes( 2 );
589
695
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
590
- 'count2'
696
+ 'count2:0'
591
697
  );
592
698
 
593
699
  act( () => screen.getByText( 'Toggle' ).click() );
594
700
 
701
+ expect( selectCount1 ).toHaveBeenCalledTimes( 1 );
702
+ expect( selectCount2 ).toHaveBeenCalledTimes( 2 );
703
+ expect( screen.getByRole( 'status' ) ).toHaveTextContent(
704
+ 'count1:0'
705
+ );
706
+
707
+ // Verify that the component subscribed to store-1 after selected from
708
+ act( () => {
709
+ registry.dispatch( 'store-1' ).inc();
710
+ } );
711
+
595
712
  expect( selectCount1 ).toHaveBeenCalledTimes( 2 );
596
713
  expect( selectCount2 ).toHaveBeenCalledTimes( 2 );
597
714
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
598
- 'count1'
715
+ 'count1:1'
599
716
  );
600
717
  } );
601
718
 
602
719
  it( "handles subscriptions to the parent's stores", () => {
603
- registry.registerStore( 'parent-store', counterStore );
720
+ registry.registerStore( 'parent-store', counterStore() );
604
721
 
605
722
  const subRegistry = createRegistry( {}, registry );
606
- subRegistry.registerStore( 'child-store', counterStore );
723
+ subRegistry.registerStore( 'child-store', counterStore() );
607
724
 
608
725
  const TestComponent = jest.fn( () => {
609
726
  const state = useSelect(
610
727
  ( select ) => ( {
611
- parentCount: select( 'parent-store' ).getCounter(),
612
- childCount: select( 'child-store' ).getCounter(),
728
+ parentCount: select( 'parent-store' ).get(),
729
+ childCount: select( 'child-store' ).get(),
613
730
  } ),
614
731
  []
615
732
  );
@@ -634,7 +751,7 @@ describe( 'useSelect', () => {
634
751
  );
635
752
 
636
753
  act( () => {
637
- registry.dispatch( 'parent-store' ).increment();
754
+ registry.dispatch( 'parent-store' ).inc();
638
755
  } );
639
756
 
640
757
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
@@ -643,13 +760,13 @@ describe( 'useSelect', () => {
643
760
  } );
644
761
 
645
762
  it( 'handles non-existing stores', () => {
646
- registry.registerStore( 'store-1', counterStore );
763
+ registry.registerStore( 'store-1', counterStore() );
647
764
 
648
765
  const TestComponent = jest.fn( () => {
649
766
  const state = useSelect(
650
767
  ( select ) => ( {
651
- count1: select( 'store-1' ).getCounter(),
652
- count2: select( 'store-2' )?.getCounter() ?? 'blank',
768
+ count1: select( 'store-1' ).get(),
769
+ count2: select( 'store-2' )?.get() ?? 'blank',
653
770
  } ),
654
771
  []
655
772
  );
@@ -672,7 +789,7 @@ describe( 'useSelect', () => {
672
789
  );
673
790
 
674
791
  act( () => {
675
- registry.dispatch( 'store-1' ).increment();
792
+ registry.dispatch( 'store-1' ).inc();
676
793
  } );
677
794
 
678
795
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
@@ -687,8 +804,7 @@ describe( 'useSelect', () => {
687
804
  const TestComponent = jest.fn( () => {
688
805
  const state = useSelect(
689
806
  ( select ) =>
690
- select( 'not-yet-registered-store' )?.getCounter() ??
691
- 'blank',
807
+ select( 'not-yet-registered-store' )?.get() ?? 'blank',
692
808
  []
693
809
  );
694
810
 
@@ -706,7 +822,7 @@ describe( 'useSelect', () => {
706
822
  act( () => {
707
823
  registry.registerStore(
708
824
  'not-yet-registered-store',
709
- counterStore
825
+ counterStore()
710
826
  );
711
827
  } );
712
828
 
@@ -714,7 +830,7 @@ describe( 'useSelect', () => {
714
830
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( 'blank' );
715
831
 
716
832
  act( () => {
717
- registry.dispatch( 'not-yet-registered-store' ).increment();
833
+ registry.dispatch( 'not-yet-registered-store' ).inc();
718
834
  } );
719
835
 
720
836
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '1' );
@@ -729,9 +845,8 @@ describe( 'useSelect', () => {
729
845
  const TestComponent = jest.fn( () => {
730
846
  const state = useSelect(
731
847
  ( select ) =>
732
- select(
733
- 'not-yet-registered-child-store'
734
- )?.getCounter() ?? 'blank',
848
+ select( 'not-yet-registered-child-store' )?.get() ??
849
+ 'blank',
735
850
  []
736
851
  );
737
852
 
@@ -751,7 +866,7 @@ describe( 'useSelect', () => {
751
866
  act( () => {
752
867
  registry.registerStore(
753
868
  'not-yet-registered-child-store',
754
- counterStore
869
+ counterStore()
755
870
  );
756
871
  } );
757
872
 
@@ -759,9 +874,7 @@ describe( 'useSelect', () => {
759
874
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( 'blank' );
760
875
 
761
876
  act( () => {
762
- registry
763
- .dispatch( 'not-yet-registered-child-store' )
764
- .increment();
877
+ registry.dispatch( 'not-yet-registered-child-store' ).inc();
765
878
  } );
766
879
 
767
880
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '1' );
@@ -778,11 +891,11 @@ describe( 'useSelect', () => {
778
891
  let counter = 0;
779
892
 
780
893
  const selectors = {
781
- getCounter: () => counter,
894
+ get: () => counter,
782
895
  };
783
896
 
784
897
  const actions = {
785
- increment: () => {
898
+ inc: () => {
786
899
  counter += 1;
787
900
  storeChanged();
788
901
  },
@@ -806,7 +919,7 @@ describe( 'useSelect', () => {
806
919
 
807
920
  const TestComponent = jest.fn( () => {
808
921
  const state = useSelect(
809
- ( select ) => select( customStore ).getCounter(),
922
+ ( select ) => select( customStore ).get(),
810
923
  []
811
924
  );
812
925
 
@@ -822,7 +935,7 @@ describe( 'useSelect', () => {
822
935
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0' );
823
936
 
824
937
  act( () => {
825
- registry.dispatch( customStore ).increment();
938
+ registry.dispatch( customStore ).inc();
826
939
  } );
827
940
 
828
941
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '1' );
@@ -832,25 +945,8 @@ describe( 'useSelect', () => {
832
945
  } );
833
946
 
834
947
  describe( 'async mode', () => {
835
- function registerCounterStore( reg, initialCount = 0 ) {
836
- reg.registerStore( 'counter', {
837
- reducer: ( state = initialCount, action ) => {
838
- if ( action.type === 'INCREMENT' ) {
839
- return state + 1;
840
- }
841
- return state;
842
- },
843
- actions: {
844
- inc: () => ( { type: 'INCREMENT' } ),
845
- },
846
- selectors: {
847
- get: ( state ) => state,
848
- },
849
- } );
850
- }
851
-
852
948
  beforeEach( () => {
853
- registerCounterStore( registry );
949
+ registry.registerStore( 'counter', counterStore() );
854
950
  } );
855
951
 
856
952
  it( 'renders with async mode', async () => {
@@ -1030,7 +1126,7 @@ describe( 'useSelect', () => {
1030
1126
 
1031
1127
  it( 'cancels scheduled updates when registry changes', async () => {
1032
1128
  const registry2 = createRegistry();
1033
- registerCounterStore( registry2, 100 );
1129
+ registry2.registerStore( 'counter', counterStore( 100 ) );
1034
1130
 
1035
1131
  const selectSpy = jest.fn( ( select ) =>
1036
1132
  select( 'counter' ).get()
@@ -1073,16 +1169,8 @@ describe( 'useSelect', () => {
1073
1169
  } );
1074
1170
 
1075
1171
  describe( 'usage without dependencies array', () => {
1076
- function registerStore( name, initial ) {
1077
- registry.registerStore( name, {
1078
- reducer: ( s = initial, a ) => ( a.type === 'inc' ? s + 1 : s ),
1079
- actions: { inc: () => ( { type: 'inc' } ) },
1080
- selectors: { get: ( s ) => s },
1081
- } );
1082
- }
1083
-
1084
1172
  it( 'does not memoize the callback when there are no deps', () => {
1085
- registerStore( 'store', 1 );
1173
+ registry.registerStore( 'store', counterStore( 1 ) );
1086
1174
 
1087
1175
  const Status = ( { multiple } ) => {
1088
1176
  const count = useSelect(
@@ -1106,9 +1194,9 @@ describe( 'useSelect', () => {
1106
1194
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '2' );
1107
1195
  } );
1108
1196
 
1109
- it( 'subscribes only stores used by the initial callback', () => {
1110
- registerStore( 'counter-1', 1 );
1111
- registerStore( 'counter-2', 10 );
1197
+ it( 'resubscribes when the set of selected stores changes', () => {
1198
+ registry.registerStore( 'counter-1', counterStore( 1 ) );
1199
+ registry.registerStore( 'counter-2', counterStore( 10 ) );
1112
1200
 
1113
1201
  const Status = ( { store } ) => {
1114
1202
  const count = useSelect( ( select ) => select( store ).get() );
@@ -1135,21 +1223,17 @@ describe( 'useSelect', () => {
1135
1223
  rerender( <App store="counter-2" /> );
1136
1224
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '10' );
1137
1225
 
1138
- // update from counter-2 is ignored because component is subcribed only to counter-1
1226
+ // update from counter-2 is processed because component has subscribed to counter-2
1139
1227
  act( () => {
1140
1228
  registry.dispatch( 'counter-2' ).inc();
1141
1229
  } );
1142
- expect( screen.getByRole( 'status' ) ).toHaveTextContent( '10' );
1230
+ expect( screen.getByRole( 'status' ) ).toHaveTextContent( '11' );
1143
1231
  } );
1144
1232
  } );
1145
1233
 
1146
1234
  describe( 'static store selection mode', () => {
1147
1235
  it( 'can read the current value from store', () => {
1148
- registry.registerStore( 'testStore', {
1149
- reducer: ( s = 0, a ) => ( a.type === 'INC' ? s + 1 : s ),
1150
- actions: { inc: () => ( { type: 'INC' } ) },
1151
- selectors: { get: ( s ) => s },
1152
- } );
1236
+ registry.registerStore( 'testStore', counterStore() );
1153
1237
 
1154
1238
  const record = jest.fn();
1155
1239