@wordpress/data 6.6.1 → 6.9.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.
@@ -162,10 +162,12 @@ export default function createReduxStore( key, options ) {
162
162
  }
163
163
 
164
164
  const resolveSelectors = mapResolveSelectors( selectors, store );
165
+ const suspendSelectors = mapSuspendSelectors( selectors, store );
165
166
 
166
167
  const getSelectors = () => selectors;
167
168
  const getActions = () => actions;
168
169
  const getResolveSelectors = () => resolveSelectors;
170
+ const getSuspendSelectors = () => suspendSelectors;
169
171
 
170
172
  // We have some modules monkey-patching the store object
171
173
  // It's wrong to do so but until we refactor all of our effects to controls
@@ -200,6 +202,7 @@ export default function createReduxStore( key, options ) {
200
202
  resolvers,
201
203
  getSelectors,
202
204
  getResolveSelectors,
205
+ getSuspendSelectors,
203
206
  getActions,
204
207
  subscribe,
205
208
  };
@@ -376,6 +379,46 @@ function mapResolveSelectors( selectors, store ) {
376
379
  } );
377
380
  }
378
381
 
382
+ /**
383
+ * Maps selectors to functions that throw a suspense promise if not yet resolved.
384
+ *
385
+ * @param {Object} selectors Selectors to map.
386
+ * @param {Object} store The redux store the selectors select from.
387
+ *
388
+ * @return {Object} Selectors mapped to their suspense functions.
389
+ */
390
+ function mapSuspendSelectors( selectors, store ) {
391
+ return mapValues( selectors, ( selector, selectorName ) => {
392
+ // Selector without a resolver doesn't have any extra suspense behavior.
393
+ if ( ! selector.hasResolver ) {
394
+ return selector;
395
+ }
396
+
397
+ return ( ...args ) => {
398
+ const result = selector.apply( null, args );
399
+
400
+ if ( selectors.hasFinishedResolution( selectorName, args ) ) {
401
+ if ( selectors.hasResolutionFailed( selectorName, args ) ) {
402
+ throw selectors.getResolutionError( selectorName, args );
403
+ }
404
+
405
+ return result;
406
+ }
407
+
408
+ throw new Promise( ( resolve ) => {
409
+ const unsubscribe = store.subscribe( () => {
410
+ if (
411
+ selectors.hasFinishedResolution( selectorName, args )
412
+ ) {
413
+ resolve();
414
+ unsubscribe();
415
+ }
416
+ } );
417
+ } );
418
+ };
419
+ } );
420
+ }
421
+
379
422
  /**
380
423
  * Returns resolvers with matched selectors for a given namespace.
381
424
  * Resolvers are side effects invoked once per argument set of a given selector call,
package/src/registry.js CHANGED
@@ -93,14 +93,16 @@ export function createRegistry( storeConfigs = {}, parent = null ) {
93
93
  return store.getSelectors();
94
94
  }
95
95
 
96
- return parent && parent.select( storeName );
96
+ return parent?.select( storeName );
97
97
  }
98
98
 
99
99
  function __unstableMarkListeningStores( callback, ref ) {
100
100
  listeningStores.clear();
101
- const result = callback.call( this );
102
- ref.current = Array.from( listeningStores );
103
- return result;
101
+ try {
102
+ return callback.call( this );
103
+ } finally {
104
+ ref.current = Array.from( listeningStores );
105
+ }
104
106
  }
105
107
 
106
108
  /**
@@ -127,6 +129,29 @@ export function createRegistry( storeConfigs = {}, parent = null ) {
127
129
  return parent && parent.resolveSelect( storeName );
128
130
  }
129
131
 
132
+ /**
133
+ * Given the name of a registered store, returns an object containing the store's
134
+ * selectors pre-bound to state so that you only need to supply additional arguments,
135
+ * and modified so that they throw promises in case the selector is not resolved yet.
136
+ *
137
+ * @param {string|StoreDescriptor} storeNameOrDescriptor Unique namespace identifier for the store
138
+ * or the store descriptor.
139
+ *
140
+ * @return {Object} Object containing the store's suspense-wrapped selectors.
141
+ */
142
+ function suspendSelect( storeNameOrDescriptor ) {
143
+ const storeName = isObject( storeNameOrDescriptor )
144
+ ? storeNameOrDescriptor.name
145
+ : storeNameOrDescriptor;
146
+ listeningStores.add( storeName );
147
+ const store = stores[ storeName ];
148
+ if ( store ) {
149
+ return store.getSuspendSelectors();
150
+ }
151
+
152
+ return parent && parent.suspendSelect( storeName );
153
+ }
154
+
130
155
  /**
131
156
  * Returns the available actions for a part of the state.
132
157
  *
@@ -276,6 +301,7 @@ export function createRegistry( storeConfigs = {}, parent = null ) {
276
301
  subscribe,
277
302
  select,
278
303
  resolveSelect,
304
+ suspendSelect,
279
305
  dispatch,
280
306
  use,
281
307
  register,
@@ -11,7 +11,14 @@ import { createRegistrySelector } from '../factory';
11
11
  import createReduxStore from '../redux-store';
12
12
  import coreDataStore from '../store';
13
13
 
14
- jest.useFakeTimers( 'legacy' );
14
+ beforeEach( () => {
15
+ jest.useFakeTimers( 'legacy' );
16
+ } );
17
+
18
+ afterEach( () => {
19
+ jest.runOnlyPendingTimers();
20
+ jest.useRealTimers();
21
+ } );
15
22
 
16
23
  describe( 'createRegistry', () => {
17
24
  let registry;
package/tsconfig.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "compilerOptions": {
4
4
  "rootDir": "src",
5
5
  "declarationDir": "build-types",
6
- "noUnusedParameters": false,
6
+ "noUnusedParameters": false
7
7
  },
8
8
  "references": [
9
9
  { "path": "../compose" },
@@ -17,6 +17,6 @@
17
17
  "src/redux-store/metadata/**/*",
18
18
  "src/promise-middleware.js",
19
19
  "src/utils",
20
- "src/*.ts"
20
+ "src/*.ts"
21
21
  ]
22
22
  }