@wordpress/data 8.5.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/build/components/use-select/index.js +90 -55
  3. package/build/components/use-select/index.js.map +1 -1
  4. package/build/components/with-dispatch/index.js +2 -0
  5. package/build/components/with-dispatch/index.js.map +1 -1
  6. package/build/components/with-select/index.js +2 -0
  7. package/build/components/with-select/index.js.map +1 -1
  8. package/build/registry.js +16 -7
  9. package/build/registry.js.map +1 -1
  10. package/build-module/components/use-select/index.js +90 -55
  11. package/build-module/components/use-select/index.js.map +1 -1
  12. package/build-module/components/with-dispatch/index.js +2 -0
  13. package/build-module/components/with-dispatch/index.js.map +1 -1
  14. package/build-module/components/with-select/index.js +2 -0
  15. package/build-module/components/with-select/index.js.map +1 -1
  16. package/build-module/registry.js +16 -7
  17. package/build-module/registry.js.map +1 -1
  18. package/build-types/components/use-select/index.d.ts.map +1 -1
  19. package/build-types/components/with-dispatch/index.d.ts +3 -1
  20. package/build-types/components/with-dispatch/index.d.ts.map +1 -1
  21. package/build-types/components/with-select/index.d.ts +3 -1
  22. package/build-types/components/with-select/index.d.ts.map +1 -1
  23. package/build-types/default-registry.d.ts +1 -1
  24. package/build-types/plugins/persistence/storage/object.d.ts +0 -3
  25. package/build-types/plugins/persistence/storage/object.d.ts.map +1 -1
  26. package/build-types/redux-store/metadata/reducer.d.ts +5 -5
  27. package/build-types/redux-store/metadata/reducer.d.ts.map +1 -1
  28. package/build-types/registry.d.ts.map +1 -1
  29. package/build-types/store/index.d.ts +0 -9
  30. package/build-types/store/index.d.ts.map +1 -1
  31. package/build-types/types.d.ts +16 -16
  32. package/build-types/types.d.ts.map +1 -1
  33. package/package.json +9 -9
  34. package/src/components/use-dispatch/test/use-dispatch.js +0 -2
  35. package/src/components/use-select/index.js +87 -57
  36. package/src/components/use-select/test/index.js +246 -164
  37. package/src/components/use-select/test/suspense.js +0 -2
  38. package/src/components/with-dispatch/index.js +2 -0
  39. package/src/components/with-dispatch/test/index.js +0 -2
  40. package/src/components/with-select/index.js +2 -0
  41. package/src/components/with-select/test/index.js +0 -2
  42. package/src/redux-store/metadata/test/selectors.js +0 -1
  43. package/src/redux-store/test/index.js +0 -2
  44. package/src/registry.js +20 -8
  45. package/src/test/controls.js +0 -2
  46. package/src/test/privateAPIs.js +0 -13
  47. package/src/test/registry.js +32 -30
  48. package/tsconfig.tsbuildinfo +1 -1
@@ -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,7 +19,18 @@ import {
19
19
  } from '../../..';
20
20
  import useSelect from '..';
21
21
 
22
- jest.useRealTimers();
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
+ }
23
34
 
24
35
  describe( 'useSelect', () => {
25
36
  let registry;
@@ -111,7 +122,7 @@ describe( 'useSelect', () => {
111
122
  );
112
123
 
113
124
  expect( selectSpyFoo ).toHaveBeenCalledTimes( 2 );
114
- expect( selectSpyBar ).toHaveBeenCalledTimes( 2 );
125
+ expect( selectSpyBar ).toHaveBeenCalledTimes( 1 );
115
126
  expect( TestComponent ).toHaveBeenCalledTimes( 3 );
116
127
 
117
128
  // Ensure expected state was rendered.
@@ -182,6 +193,81 @@ describe( 'useSelect', () => {
182
193
  expect( Child ).toHaveBeenCalledTimes( 1 );
183
194
  } );
184
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
+
185
271
  describe( 'rerenders as expected with various mapSelect return types', () => {
186
272
  const getComponent = ( mapSelectSpy ) => () => {
187
273
  const data = useSelect( mapSelectSpy, [] );
@@ -256,40 +342,20 @@ describe( 'useSelect', () => {
256
342
  } );
257
343
 
258
344
  describe( 're-calls the selector as few times as possible', () => {
259
- const counterStore = {
260
- actions: {
261
- increment: () => ( { type: 'INCREMENT' } ),
262
- },
263
- reducer: ( state, action ) => {
264
- if ( ! state ) {
265
- return { counter: 0 };
266
- }
267
- if ( action?.type === 'INCREMENT' ) {
268
- return { counter: state.counter + 1 };
269
- }
270
- return state;
271
- },
272
- selectors: {
273
- getCounter: ( state ) => state.counter,
274
- },
275
- };
276
-
277
345
  it( 'only calls the selectors it has selected', () => {
278
- registry.registerStore( 'store-1', counterStore );
279
- registry.registerStore( 'store-2', counterStore );
346
+ registry.registerStore( 'store-1', counterStore() );
347
+ registry.registerStore( 'store-2', counterStore() );
280
348
 
281
349
  const selectCount1 = jest.fn();
282
350
  const selectCount2 = jest.fn();
283
351
 
284
352
  const TestComponent = jest.fn( () => {
285
353
  const count1 = useSelect(
286
- ( select ) =>
287
- selectCount1() || select( 'store-1' ).getCounter(),
354
+ ( select ) => selectCount1() || select( 'store-1' ).get(),
288
355
  []
289
356
  );
290
357
  useSelect(
291
- ( select ) =>
292
- selectCount2() || select( 'store-2' ).getCounter(),
358
+ ( select ) => selectCount2() || select( 'store-2' ).get(),
293
359
  []
294
360
  );
295
361
 
@@ -308,7 +374,7 @@ describe( 'useSelect', () => {
308
374
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0' );
309
375
 
310
376
  act( () => {
311
- registry.dispatch( 'store-2' ).increment();
377
+ registry.dispatch( 'store-2' ).inc();
312
378
  } );
313
379
 
314
380
  expect( selectCount1 ).toHaveBeenCalledTimes( 2 );
@@ -317,7 +383,7 @@ describe( 'useSelect', () => {
317
383
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0' );
318
384
 
319
385
  act( () => {
320
- registry.dispatch( 'store-1' ).increment();
386
+ registry.dispatch( 'store-1' ).inc();
321
387
  } );
322
388
 
323
389
  expect( selectCount1 ).toHaveBeenCalledTimes( 3 );
@@ -330,9 +396,9 @@ describe( 'useSelect', () => {
330
396
  } );
331
397
 
332
398
  it( 'can subscribe to multiple stores at once', () => {
333
- registry.registerStore( 'store-1', counterStore );
334
- registry.registerStore( 'store-2', counterStore );
335
- registry.registerStore( 'store-3', counterStore );
399
+ registry.registerStore( 'store-1', counterStore() );
400
+ registry.registerStore( 'store-2', counterStore() );
401
+ registry.registerStore( 'store-3', counterStore() );
336
402
 
337
403
  const selectCount1And2 = jest.fn();
338
404
 
@@ -340,8 +406,8 @@ describe( 'useSelect', () => {
340
406
  const { count1, count2 } = useSelect(
341
407
  ( select ) =>
342
408
  selectCount1And2() || {
343
- count1: select( 'store-1' ).getCounter(),
344
- count2: select( 'store-2' ).getCounter(),
409
+ count1: select( 'store-1' ).get(),
410
+ count2: select( 'store-2' ).get(),
345
411
  },
346
412
  []
347
413
  );
@@ -363,14 +429,14 @@ describe( 'useSelect', () => {
363
429
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0,0' );
364
430
 
365
431
  act( () => {
366
- registry.dispatch( 'store-2' ).increment();
432
+ registry.dispatch( 'store-2' ).inc();
367
433
  } );
368
434
 
369
435
  expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 );
370
436
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0,1' );
371
437
 
372
438
  act( () => {
373
- registry.dispatch( 'store-3' ).increment();
439
+ registry.dispatch( 'store-3' ).inc();
374
440
  } );
375
441
 
376
442
  expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 );
@@ -378,9 +444,9 @@ describe( 'useSelect', () => {
378
444
  } );
379
445
 
380
446
  it( 're-calls the selector when deps changed', () => {
381
- registry.registerStore( 'store-1', counterStore );
382
- registry.registerStore( 'store-2', counterStore );
383
- registry.registerStore( 'store-3', counterStore );
447
+ registry.registerStore( 'store-1', counterStore() );
448
+ registry.registerStore( 'store-2', counterStore() );
449
+ registry.registerStore( 'store-3', counterStore() );
384
450
 
385
451
  let dep, setDep;
386
452
  const selectCount1AndDep = jest.fn();
@@ -390,7 +456,7 @@ describe( 'useSelect', () => {
390
456
  const state = useSelect(
391
457
  ( select ) =>
392
458
  selectCount1AndDep() || {
393
- count1: select( 'store-1' ).getCounter(),
459
+ count1: select( 'store-1' ).get(),
394
460
  dep,
395
461
  },
396
462
  [ dep ]
@@ -418,63 +484,40 @@ describe( 'useSelect', () => {
418
484
  setDep( 1 );
419
485
  } );
420
486
 
421
- expect( selectCount1AndDep ).toHaveBeenCalledTimes( 4 );
487
+ expect( selectCount1AndDep ).toHaveBeenCalledTimes( 3 );
422
488
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
423
489
  'count:0,dep:1'
424
490
  );
425
491
 
426
492
  act( () => {
427
- registry.dispatch( 'store-1' ).increment();
493
+ registry.dispatch( 'store-1' ).inc();
428
494
  } );
429
495
 
430
- expect( selectCount1AndDep ).toHaveBeenCalledTimes( 5 );
496
+ expect( selectCount1AndDep ).toHaveBeenCalledTimes( 4 );
431
497
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
432
498
  'count:1,dep:1'
433
499
  );
434
500
  } );
435
501
 
436
- it( 'captures state changes scheduled between render and effect', () => {
437
- registry.registerStore( 'store-1', counterStore );
438
-
439
- class ChildComponent extends Component {
440
- componentDidUpdate( prevProps ) {
441
- if (
442
- this.props.childShouldDispatch &&
443
- this.props.childShouldDispatch !==
444
- prevProps.childShouldDispatch
445
- ) {
446
- registry.dispatch( 'store-1' ).increment();
447
- }
448
- }
449
-
450
- render() {
451
- return null;
452
- }
453
- }
502
+ it( 'captures state changes scheduled between render and subscription', () => {
503
+ registry.registerStore( 'store-1', counterStore() );
454
504
 
455
- const selectCount1AndDep = jest.fn( ( select ) => ( {
456
- count1: select( 'store-1' ).getCounter(),
505
+ const selectCount1 = jest.fn( ( select ) => ( {
506
+ count1: select( 'store-1' ).get(),
457
507
  } ) );
458
508
 
459
- const TestComponent = () => {
460
- const [ childShouldDispatch, setChildShouldDispatch ] =
461
- useState( false );
462
- const state = useSelect( selectCount1AndDep, [] );
509
+ const TestComponent = jest.fn( () => {
510
+ const { count1 } = useSelect( selectCount1, [] );
463
511
 
464
- return (
465
- <>
466
- <div role="status">count1:{ state.count1 }</div>
467
- <ChildComponent
468
- childShouldDispatch={ childShouldDispatch }
469
- />
470
- <button
471
- onClick={ () => setChildShouldDispatch( true ) }
472
- >
473
- triggerChildDispatch
474
- </button>
475
- </>
476
- );
477
- };
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
+ } );
478
521
 
479
522
  render(
480
523
  <RegistryProvider value={ registry }>
@@ -482,30 +525,93 @@ describe( 'useSelect', () => {
482
525
  </RegistryProvider>
483
526
  );
484
527
 
485
- 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 );
486
531
 
487
- 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.
488
536
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
489
537
  'count1:1'
490
538
  );
491
539
  } );
492
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
+
493
603
  it( 'handles registry selectors', () => {
494
604
  const getCount1And2 = createRegistrySelector(
495
605
  ( select ) => ( state ) => ( {
496
- count1: state.counter,
497
- count2: select( 'store-2' ).getCounter(),
606
+ count1: state,
607
+ count2: select( 'store-2' ).get(),
498
608
  } )
499
609
  );
500
610
 
501
- registry.registerStore( 'store-1', {
502
- ...counterStore,
503
- selectors: {
504
- ...counterStore.selectors,
505
- getCount1And2,
506
- },
507
- } );
508
- 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() );
509
615
 
510
616
  const selectCount1And2 = jest.fn();
511
617
 
@@ -536,7 +642,7 @@ describe( 'useSelect', () => {
536
642
  );
537
643
 
538
644
  act( () => {
539
- registry.dispatch( 'store-2' ).increment();
645
+ registry.dispatch( 'store-2' ).inc();
540
646
  } );
541
647
 
542
648
  expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 );
@@ -546,8 +652,8 @@ describe( 'useSelect', () => {
546
652
  } );
547
653
 
548
654
  it( 'handles conditional statements in selectors', () => {
549
- registry.registerStore( 'store-1', counterStore );
550
- registry.registerStore( 'store-2', counterStore );
655
+ registry.registerStore( 'store-1', counterStore() );
656
+ registry.registerStore( 'store-2', counterStore() );
551
657
 
552
658
  const selectCount1 = jest.fn();
553
659
  const selectCount2 = jest.fn();
@@ -561,13 +667,11 @@ describe( 'useSelect', () => {
561
667
  ( select ) => {
562
668
  if ( shouldSelectCount1 ) {
563
669
  selectCount1();
564
- select( 'store-1' ).getCounter();
565
- return 'count1';
670
+ return 'count1:' + select( 'store-1' ).get();
566
671
  }
567
672
 
568
673
  selectCount2();
569
- select( 'store-2' ).getCounter();
570
- return 'count2';
674
+ return 'count2:' + select( 'store-2' ).get();
571
675
  },
572
676
  [ shouldSelectCount1 ]
573
677
  );
@@ -589,29 +693,40 @@ describe( 'useSelect', () => {
589
693
  expect( selectCount1 ).toHaveBeenCalledTimes( 0 );
590
694
  expect( selectCount2 ).toHaveBeenCalledTimes( 2 );
591
695
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
592
- 'count2'
696
+ 'count2:0'
593
697
  );
594
698
 
595
699
  act( () => screen.getByText( 'Toggle' ).click() );
596
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
+
597
712
  expect( selectCount1 ).toHaveBeenCalledTimes( 2 );
598
713
  expect( selectCount2 ).toHaveBeenCalledTimes( 2 );
599
714
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
600
- 'count1'
715
+ 'count1:1'
601
716
  );
602
717
  } );
603
718
 
604
719
  it( "handles subscriptions to the parent's stores", () => {
605
- registry.registerStore( 'parent-store', counterStore );
720
+ registry.registerStore( 'parent-store', counterStore() );
606
721
 
607
722
  const subRegistry = createRegistry( {}, registry );
608
- subRegistry.registerStore( 'child-store', counterStore );
723
+ subRegistry.registerStore( 'child-store', counterStore() );
609
724
 
610
725
  const TestComponent = jest.fn( () => {
611
726
  const state = useSelect(
612
727
  ( select ) => ( {
613
- parentCount: select( 'parent-store' ).getCounter(),
614
- childCount: select( 'child-store' ).getCounter(),
728
+ parentCount: select( 'parent-store' ).get(),
729
+ childCount: select( 'child-store' ).get(),
615
730
  } ),
616
731
  []
617
732
  );
@@ -636,7 +751,7 @@ describe( 'useSelect', () => {
636
751
  );
637
752
 
638
753
  act( () => {
639
- registry.dispatch( 'parent-store' ).increment();
754
+ registry.dispatch( 'parent-store' ).inc();
640
755
  } );
641
756
 
642
757
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
@@ -645,13 +760,13 @@ describe( 'useSelect', () => {
645
760
  } );
646
761
 
647
762
  it( 'handles non-existing stores', () => {
648
- registry.registerStore( 'store-1', counterStore );
763
+ registry.registerStore( 'store-1', counterStore() );
649
764
 
650
765
  const TestComponent = jest.fn( () => {
651
766
  const state = useSelect(
652
767
  ( select ) => ( {
653
- count1: select( 'store-1' ).getCounter(),
654
- count2: select( 'store-2' )?.getCounter() ?? 'blank',
768
+ count1: select( 'store-1' ).get(),
769
+ count2: select( 'store-2' )?.get() ?? 'blank',
655
770
  } ),
656
771
  []
657
772
  );
@@ -674,7 +789,7 @@ describe( 'useSelect', () => {
674
789
  );
675
790
 
676
791
  act( () => {
677
- registry.dispatch( 'store-1' ).increment();
792
+ registry.dispatch( 'store-1' ).inc();
678
793
  } );
679
794
 
680
795
  expect( screen.getByRole( 'status' ) ).toHaveTextContent(
@@ -689,8 +804,7 @@ describe( 'useSelect', () => {
689
804
  const TestComponent = jest.fn( () => {
690
805
  const state = useSelect(
691
806
  ( select ) =>
692
- select( 'not-yet-registered-store' )?.getCounter() ??
693
- 'blank',
807
+ select( 'not-yet-registered-store' )?.get() ?? 'blank',
694
808
  []
695
809
  );
696
810
 
@@ -708,7 +822,7 @@ describe( 'useSelect', () => {
708
822
  act( () => {
709
823
  registry.registerStore(
710
824
  'not-yet-registered-store',
711
- counterStore
825
+ counterStore()
712
826
  );
713
827
  } );
714
828
 
@@ -716,7 +830,7 @@ describe( 'useSelect', () => {
716
830
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( 'blank' );
717
831
 
718
832
  act( () => {
719
- registry.dispatch( 'not-yet-registered-store' ).increment();
833
+ registry.dispatch( 'not-yet-registered-store' ).inc();
720
834
  } );
721
835
 
722
836
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '1' );
@@ -731,9 +845,8 @@ describe( 'useSelect', () => {
731
845
  const TestComponent = jest.fn( () => {
732
846
  const state = useSelect(
733
847
  ( select ) =>
734
- select(
735
- 'not-yet-registered-child-store'
736
- )?.getCounter() ?? 'blank',
848
+ select( 'not-yet-registered-child-store' )?.get() ??
849
+ 'blank',
737
850
  []
738
851
  );
739
852
 
@@ -753,7 +866,7 @@ describe( 'useSelect', () => {
753
866
  act( () => {
754
867
  registry.registerStore(
755
868
  'not-yet-registered-child-store',
756
- counterStore
869
+ counterStore()
757
870
  );
758
871
  } );
759
872
 
@@ -761,9 +874,7 @@ describe( 'useSelect', () => {
761
874
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( 'blank' );
762
875
 
763
876
  act( () => {
764
- registry
765
- .dispatch( 'not-yet-registered-child-store' )
766
- .increment();
877
+ registry.dispatch( 'not-yet-registered-child-store' ).inc();
767
878
  } );
768
879
 
769
880
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '1' );
@@ -780,11 +891,11 @@ describe( 'useSelect', () => {
780
891
  let counter = 0;
781
892
 
782
893
  const selectors = {
783
- getCounter: () => counter,
894
+ get: () => counter,
784
895
  };
785
896
 
786
897
  const actions = {
787
- increment: () => {
898
+ inc: () => {
788
899
  counter += 1;
789
900
  storeChanged();
790
901
  },
@@ -808,7 +919,7 @@ describe( 'useSelect', () => {
808
919
 
809
920
  const TestComponent = jest.fn( () => {
810
921
  const state = useSelect(
811
- ( select ) => select( customStore ).getCounter(),
922
+ ( select ) => select( customStore ).get(),
812
923
  []
813
924
  );
814
925
 
@@ -824,7 +935,7 @@ describe( 'useSelect', () => {
824
935
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '0' );
825
936
 
826
937
  act( () => {
827
- registry.dispatch( customStore ).increment();
938
+ registry.dispatch( customStore ).inc();
828
939
  } );
829
940
 
830
941
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '1' );
@@ -834,25 +945,8 @@ describe( 'useSelect', () => {
834
945
  } );
835
946
 
836
947
  describe( 'async mode', () => {
837
- function registerCounterStore( reg, initialCount = 0 ) {
838
- reg.registerStore( 'counter', {
839
- reducer: ( state = initialCount, action ) => {
840
- if ( action.type === 'INCREMENT' ) {
841
- return state + 1;
842
- }
843
- return state;
844
- },
845
- actions: {
846
- inc: () => ( { type: 'INCREMENT' } ),
847
- },
848
- selectors: {
849
- get: ( state ) => state,
850
- },
851
- } );
852
- }
853
-
854
948
  beforeEach( () => {
855
- registerCounterStore( registry );
949
+ registry.registerStore( 'counter', counterStore() );
856
950
  } );
857
951
 
858
952
  it( 'renders with async mode', async () => {
@@ -1032,7 +1126,7 @@ describe( 'useSelect', () => {
1032
1126
 
1033
1127
  it( 'cancels scheduled updates when registry changes', async () => {
1034
1128
  const registry2 = createRegistry();
1035
- registerCounterStore( registry2, 100 );
1129
+ registry2.registerStore( 'counter', counterStore( 100 ) );
1036
1130
 
1037
1131
  const selectSpy = jest.fn( ( select ) =>
1038
1132
  select( 'counter' ).get()
@@ -1075,16 +1169,8 @@ describe( 'useSelect', () => {
1075
1169
  } );
1076
1170
 
1077
1171
  describe( 'usage without dependencies array', () => {
1078
- function registerStore( name, initial ) {
1079
- registry.registerStore( name, {
1080
- reducer: ( s = initial, a ) => ( a.type === 'inc' ? s + 1 : s ),
1081
- actions: { inc: () => ( { type: 'inc' } ) },
1082
- selectors: { get: ( s ) => s },
1083
- } );
1084
- }
1085
-
1086
1172
  it( 'does not memoize the callback when there are no deps', () => {
1087
- registerStore( 'store', 1 );
1173
+ registry.registerStore( 'store', counterStore( 1 ) );
1088
1174
 
1089
1175
  const Status = ( { multiple } ) => {
1090
1176
  const count = useSelect(
@@ -1108,9 +1194,9 @@ describe( 'useSelect', () => {
1108
1194
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '2' );
1109
1195
  } );
1110
1196
 
1111
- it( 'subscribes only stores used by the initial callback', () => {
1112
- registerStore( 'counter-1', 1 );
1113
- 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 ) );
1114
1200
 
1115
1201
  const Status = ( { store } ) => {
1116
1202
  const count = useSelect( ( select ) => select( store ).get() );
@@ -1137,21 +1223,17 @@ describe( 'useSelect', () => {
1137
1223
  rerender( <App store="counter-2" /> );
1138
1224
  expect( screen.getByRole( 'status' ) ).toHaveTextContent( '10' );
1139
1225
 
1140
- // 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
1141
1227
  act( () => {
1142
1228
  registry.dispatch( 'counter-2' ).inc();
1143
1229
  } );
1144
- expect( screen.getByRole( 'status' ) ).toHaveTextContent( '10' );
1230
+ expect( screen.getByRole( 'status' ) ).toHaveTextContent( '11' );
1145
1231
  } );
1146
1232
  } );
1147
1233
 
1148
1234
  describe( 'static store selection mode', () => {
1149
1235
  it( 'can read the current value from store', () => {
1150
- registry.registerStore( 'testStore', {
1151
- reducer: ( s = 0, a ) => ( a.type === 'INC' ? s + 1 : s ),
1152
- actions: { inc: () => ( { type: 'INC' } ) },
1153
- selectors: { get: ( s ) => s },
1154
- } );
1236
+ registry.registerStore( 'testStore', counterStore() );
1155
1237
 
1156
1238
  const record = jest.fn();
1157
1239