@wordpress/data 10.24.0 → 10.26.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.
- package/CHANGELOG.md +8 -0
- package/build/redux-store/index.js +175 -120
- package/build/redux-store/index.js.map +1 -1
- package/build-module/redux-store/index.js +175 -120
- package/build-module/redux-store/index.js.map +1 -1
- package/build-types/redux-store/index.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/redux-store/index.js +229 -124
- package/src/redux-store/test/index.js +4 -3
- package/src/test/privateAPIs.js +46 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/redux-store/index.js
CHANGED
|
@@ -108,14 +108,18 @@ function createResolversCache() {
|
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
function createBindingCache(
|
|
111
|
+
function createBindingCache( getItem, bindItem ) {
|
|
112
112
|
const cache = new WeakMap();
|
|
113
113
|
|
|
114
114
|
return {
|
|
115
|
-
get(
|
|
115
|
+
get( itemName ) {
|
|
116
|
+
const item = getItem( itemName );
|
|
117
|
+
if ( ! item ) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
116
120
|
let boundItem = cache.get( item );
|
|
117
121
|
if ( ! boundItem ) {
|
|
118
|
-
boundItem =
|
|
122
|
+
boundItem = bindItem( item, itemName );
|
|
119
123
|
cache.set( item, boundItem );
|
|
120
124
|
}
|
|
121
125
|
return boundItem;
|
|
@@ -123,6 +127,13 @@ function createBindingCache( bind ) {
|
|
|
123
127
|
};
|
|
124
128
|
}
|
|
125
129
|
|
|
130
|
+
function createPrivateProxy( publicItems, privateItems ) {
|
|
131
|
+
return new Proxy( publicItems, {
|
|
132
|
+
get: ( target, itemName ) =>
|
|
133
|
+
privateItems.get( itemName ) || Reflect.get( target, itemName ),
|
|
134
|
+
} );
|
|
135
|
+
}
|
|
136
|
+
|
|
126
137
|
/**
|
|
127
138
|
* Creates a data store descriptor for the provided Redux store configuration containing
|
|
128
139
|
* properties describing reducer, actions, selectors, controls and resolvers.
|
|
@@ -179,16 +190,20 @@ export default function createReduxStore( key, options ) {
|
|
|
179
190
|
*/
|
|
180
191
|
const listeners = new Set();
|
|
181
192
|
const reducer = options.reducer;
|
|
193
|
+
|
|
194
|
+
// Object that every thunk function receives as the first argument. It contains the
|
|
195
|
+
// `registry`, `dispatch`, `select` and `resolveSelect` fields. Some of them are
|
|
196
|
+
// constructed as getters to avoid circular dependencies.
|
|
182
197
|
const thunkArgs = {
|
|
183
198
|
registry,
|
|
184
199
|
get dispatch() {
|
|
185
|
-
return
|
|
200
|
+
return thunkDispatch;
|
|
186
201
|
},
|
|
187
202
|
get select() {
|
|
188
|
-
return
|
|
203
|
+
return thunkSelect;
|
|
189
204
|
},
|
|
190
205
|
get resolveSelect() {
|
|
191
|
-
return
|
|
206
|
+
return resolveSelectors;
|
|
192
207
|
},
|
|
193
208
|
};
|
|
194
209
|
|
|
@@ -198,42 +213,61 @@ export default function createReduxStore( key, options ) {
|
|
|
198
213
|
registry,
|
|
199
214
|
thunkArgs
|
|
200
215
|
);
|
|
216
|
+
|
|
201
217
|
// Expose the private registration functions on the store
|
|
202
218
|
// so they can be copied to a sub registry in registry.js.
|
|
203
219
|
lock( store, privateRegistrationFunctions );
|
|
204
220
|
const resolversCache = createResolversCache();
|
|
205
221
|
|
|
222
|
+
// Binds an action creator (`action`) to the `store`, making it a callable function.
|
|
223
|
+
// These are the functions that are returned by `useDispatch`, for example.
|
|
224
|
+
// It always returns a `Promise`, although actions are not always async. That's an
|
|
225
|
+
// unfortunate backward compatibility measure.
|
|
206
226
|
function bindAction( action ) {
|
|
207
227
|
return ( ...args ) =>
|
|
208
228
|
Promise.resolve( store.dispatch( action( ...args ) ) );
|
|
209
229
|
}
|
|
210
230
|
|
|
231
|
+
/*
|
|
232
|
+
* Object with all public actions, both metadata and store actions.
|
|
233
|
+
*/
|
|
211
234
|
const actions = {
|
|
212
235
|
...mapValues( metadataActions, bindAction ),
|
|
213
236
|
...mapValues( options.actions, bindAction ),
|
|
214
237
|
};
|
|
215
238
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
239
|
+
// Object with both public and private actions. Private actions are accessed through a proxy,
|
|
240
|
+
// which looks them up in real time on the `privateActions` object. That's because private
|
|
241
|
+
// actions can be registered at any time with `registerPrivateActions`. Also once a private
|
|
242
|
+
// action creator is bound to the store, it is cached to give it a stable identity.
|
|
243
|
+
const allActions = createPrivateProxy(
|
|
244
|
+
actions,
|
|
245
|
+
createBindingCache(
|
|
246
|
+
( name ) => privateActions[ name ],
|
|
247
|
+
bindAction
|
|
248
|
+
)
|
|
249
|
+
);
|
|
225
250
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
251
|
+
// An object that implements the `dispatch` object that is passed to thunk functions.
|
|
252
|
+
// It is callable (`dispatch( action )`) and also has methods (`dispatch.foo()`) that
|
|
253
|
+
// correspond to bound registered actions, both public and private. Implemented with the proxy
|
|
254
|
+
// `get` method, delegating to `allActions`.
|
|
255
|
+
const thunkDispatch = new Proxy(
|
|
256
|
+
( action ) => store.dispatch( action ),
|
|
257
|
+
{ get: ( target, name ) => allActions[ name ] }
|
|
258
|
+
);
|
|
230
259
|
|
|
260
|
+
// To the public `actions` object, add the "locked" `allActions` object. When used,
|
|
261
|
+
// `unlock( actions )` will return `allActions`, implementing a way how to get at the private actions.
|
|
231
262
|
lock( actions, allActions );
|
|
232
263
|
|
|
264
|
+
// If we have selector resolvers, convert them to a normalized form.
|
|
233
265
|
const resolvers = options.resolvers
|
|
234
|
-
?
|
|
266
|
+
? mapValues( options.resolvers, mapResolver )
|
|
235
267
|
: {};
|
|
236
268
|
|
|
269
|
+
// Bind a selector to the store. Call the selector with the current state, correct registry,
|
|
270
|
+
// and if there is a resolver, attach the resolver logic to the selector.
|
|
237
271
|
function bindSelector( selector, selectorName ) {
|
|
238
272
|
if ( selector.isRegistrySelector ) {
|
|
239
273
|
selector.registry = registry;
|
|
@@ -241,8 +275,7 @@ export default function createReduxStore( key, options ) {
|
|
|
241
275
|
const boundSelector = ( ...args ) => {
|
|
242
276
|
args = normalize( selector, args );
|
|
243
277
|
const state = store.__unstableOriginalGetState();
|
|
244
|
-
// Before calling the selector, switch to the correct
|
|
245
|
-
// registry.
|
|
278
|
+
// Before calling the selector, switch to the correct registry.
|
|
246
279
|
if ( selector.isRegistrySelector ) {
|
|
247
280
|
selector.registry = registry;
|
|
248
281
|
}
|
|
@@ -267,66 +300,137 @@ export default function createReduxStore( key, options ) {
|
|
|
267
300
|
selectorName,
|
|
268
301
|
resolver,
|
|
269
302
|
store,
|
|
270
|
-
resolversCache
|
|
303
|
+
resolversCache,
|
|
304
|
+
boundMetadataSelectors
|
|
271
305
|
);
|
|
272
306
|
}
|
|
273
307
|
|
|
308
|
+
// Metadata selectors are bound differently: different state (`state.metadata`), no resolvers,
|
|
309
|
+
// normalization depending on the target selector.
|
|
274
310
|
function bindMetadataSelector( metaDataSelector ) {
|
|
275
|
-
const boundSelector = (
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const targetSelector =
|
|
281
|
-
options?.selectors?.[ originalSelectorName ];
|
|
282
|
-
|
|
311
|
+
const boundSelector = (
|
|
312
|
+
selectorName,
|
|
313
|
+
selectorArgs,
|
|
314
|
+
...args
|
|
315
|
+
) => {
|
|
283
316
|
// Normalize the arguments passed to the target selector.
|
|
284
|
-
if (
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
317
|
+
if ( selectorName ) {
|
|
318
|
+
const targetSelector =
|
|
319
|
+
options.selectors?.[ selectorName ];
|
|
320
|
+
if ( targetSelector ) {
|
|
321
|
+
selectorArgs = normalize(
|
|
322
|
+
targetSelector,
|
|
323
|
+
selectorArgs
|
|
324
|
+
);
|
|
325
|
+
}
|
|
289
326
|
}
|
|
290
327
|
|
|
291
|
-
|
|
328
|
+
const state = store.__unstableOriginalGetState();
|
|
329
|
+
|
|
330
|
+
return metaDataSelector(
|
|
331
|
+
state.metadata,
|
|
332
|
+
selectorName,
|
|
333
|
+
selectorArgs,
|
|
334
|
+
...args
|
|
335
|
+
);
|
|
292
336
|
};
|
|
293
337
|
boundSelector.hasResolver = false;
|
|
294
338
|
return boundSelector;
|
|
295
339
|
}
|
|
296
340
|
|
|
341
|
+
// Perform binding of both metadata and store selectors and combine them in one
|
|
342
|
+
// `selectors` object. These are all public selectors of the store.
|
|
343
|
+
const boundMetadataSelectors = mapValues(
|
|
344
|
+
metadataSelectors,
|
|
345
|
+
bindMetadataSelector
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
const boundSelectors = mapValues( options.selectors, bindSelector );
|
|
349
|
+
|
|
297
350
|
const selectors = {
|
|
298
|
-
...
|
|
299
|
-
...
|
|
351
|
+
...boundMetadataSelectors,
|
|
352
|
+
...boundSelectors,
|
|
300
353
|
};
|
|
301
354
|
|
|
302
|
-
|
|
355
|
+
// Cache of bould private selectors. They are bound only when first accessed, because
|
|
356
|
+
// new private selectors can be registered at any time (with `registerPrivateSelectors`).
|
|
357
|
+
// Once bound, they are cached to give them a stable identity.
|
|
358
|
+
const boundPrivateSelectors = createBindingCache(
|
|
359
|
+
( name ) => privateSelectors[ name ],
|
|
360
|
+
bindSelector
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
const allSelectors = createPrivateProxy(
|
|
364
|
+
selectors,
|
|
365
|
+
boundPrivateSelectors
|
|
366
|
+
);
|
|
303
367
|
|
|
304
368
|
// Pre-bind the private selectors that have been registered by the time of
|
|
305
369
|
// instantiation, so that registry selectors are bound to the registry.
|
|
306
|
-
for ( const
|
|
307
|
-
|
|
308
|
-
) ) {
|
|
309
|
-
boundPrivateSelectors.get( selector, selectorName );
|
|
370
|
+
for ( const selectorName of Object.keys( privateSelectors ) ) {
|
|
371
|
+
boundPrivateSelectors.get( selectorName );
|
|
310
372
|
}
|
|
311
373
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const thunkSelectors = new Proxy( allSelectors, {
|
|
322
|
-
apply: ( target, thisArg, [ selector ] ) =>
|
|
323
|
-
selector( store.__unstableOriginalGetState() ),
|
|
324
|
-
} );
|
|
374
|
+
// An object that implements the `select` object that is passed to thunk functions.
|
|
375
|
+
// It is callable (`select( selector )`) and also has methods (`select.foo()`) that
|
|
376
|
+
// correspond to bound registered selectors, both public and private. Implemented with the proxy
|
|
377
|
+
// `get` method, delegating to `allSelectors`.
|
|
378
|
+
const thunkSelect = new Proxy(
|
|
379
|
+
( selector ) => selector( store.__unstableOriginalGetState() ),
|
|
380
|
+
{ get: ( target, name ) => allSelectors[ name ] }
|
|
381
|
+
);
|
|
325
382
|
|
|
383
|
+
// To the public `selectors` object, add the "locked" `allSelectors` object. When used,
|
|
384
|
+
// `unlock( selectors )` will return `allSelectors`, implementing a way how to get at the private selectors.
|
|
326
385
|
lock( selectors, allSelectors );
|
|
327
386
|
|
|
328
|
-
|
|
329
|
-
|
|
387
|
+
// For each selector, create a function that calls the selector, waits for resolution and returns
|
|
388
|
+
// a promise that resolves when the resolution is finished.
|
|
389
|
+
const bindResolveSelector = mapResolveSelector(
|
|
390
|
+
store,
|
|
391
|
+
boundMetadataSelectors
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
// Now apply this function to all bound selectors, public and private. We are excluding
|
|
395
|
+
// metadata selectors because they don't have resolvers.
|
|
396
|
+
const resolveSelectors = mapValues(
|
|
397
|
+
boundSelectors,
|
|
398
|
+
bindResolveSelector
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
const allResolveSelectors = createPrivateProxy(
|
|
402
|
+
resolveSelectors,
|
|
403
|
+
createBindingCache(
|
|
404
|
+
( name ) => boundPrivateSelectors.get( name ),
|
|
405
|
+
bindResolveSelector
|
|
406
|
+
)
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
// Lock the selectors so that `unlock( resolveSelectors )` returns `allResolveSelectors`.
|
|
410
|
+
lock( resolveSelectors, allResolveSelectors );
|
|
411
|
+
|
|
412
|
+
// Now, in a way very similar to `bindResolveSelector`, we create a function that maps
|
|
413
|
+
// selectors to functions that throw a suspense promise if not yet resolved.
|
|
414
|
+
const bindSuspendSelector = mapSuspendSelector(
|
|
415
|
+
store,
|
|
416
|
+
boundMetadataSelectors
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
const suspendSelectors = {
|
|
420
|
+
...boundMetadataSelectors, // no special suspense behavior
|
|
421
|
+
...mapValues( boundSelectors, bindSuspendSelector ),
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const allSuspendSelectors = createPrivateProxy(
|
|
425
|
+
suspendSelectors,
|
|
426
|
+
createBindingCache(
|
|
427
|
+
( name ) => boundPrivateSelectors.get( name ),
|
|
428
|
+
bindSuspendSelector
|
|
429
|
+
)
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
// Lock the selectors so that `unlock( suspendSelectors )` returns 'allSuspendSelectors`.
|
|
433
|
+
lock( suspendSelectors, allSuspendSelectors );
|
|
330
434
|
|
|
331
435
|
const getSelectors = () => selectors;
|
|
332
436
|
const getActions = () => actions;
|
|
@@ -381,7 +485,7 @@ export default function createReduxStore( key, options ) {
|
|
|
381
485
|
|
|
382
486
|
// Expose the private registration functions on the store
|
|
383
487
|
// descriptor. That's a natural choice since that's where the
|
|
384
|
-
// public actions and selectors are stored
|
|
488
|
+
// public actions and selectors are stored.
|
|
385
489
|
lock( storeDescriptor, privateRegistrationFunctions );
|
|
386
490
|
|
|
387
491
|
return storeDescriptor;
|
|
@@ -445,46 +549,37 @@ function instantiateReduxStore( key, options, registry, thunkArgs ) {
|
|
|
445
549
|
}
|
|
446
550
|
|
|
447
551
|
/**
|
|
448
|
-
* Maps selectors to functions that return a resolution promise for them
|
|
552
|
+
* Maps selectors to functions that return a resolution promise for them.
|
|
449
553
|
*
|
|
450
|
-
* @param {Object} selectors
|
|
451
|
-
* @param {Object}
|
|
554
|
+
* @param {Object} store The redux store the selectors are bound to.
|
|
555
|
+
* @param {Object} boundMetadataSelectors The bound metadata selectors.
|
|
452
556
|
*
|
|
453
|
-
* @return {
|
|
557
|
+
* @return {Function} Function that maps selectors to resolvers.
|
|
454
558
|
*/
|
|
455
|
-
function
|
|
456
|
-
|
|
457
|
-
getIsResolving,
|
|
458
|
-
hasStartedResolution,
|
|
459
|
-
hasFinishedResolution,
|
|
460
|
-
hasResolutionFailed,
|
|
461
|
-
isResolving,
|
|
462
|
-
getCachedResolvers,
|
|
463
|
-
getResolutionState,
|
|
464
|
-
getResolutionError,
|
|
465
|
-
hasResolvingSelectors,
|
|
466
|
-
countSelectorsByStatus,
|
|
467
|
-
...storeSelectors
|
|
468
|
-
} = selectors;
|
|
469
|
-
|
|
470
|
-
return mapValues( storeSelectors, ( selector, selectorName ) => {
|
|
559
|
+
function mapResolveSelector( store, boundMetadataSelectors ) {
|
|
560
|
+
return ( selector, selectorName ) => {
|
|
471
561
|
// If the selector doesn't have a resolver, just convert the return value
|
|
472
562
|
// (including exceptions) to a Promise, no additional extra behavior is needed.
|
|
473
563
|
if ( ! selector.hasResolver ) {
|
|
474
564
|
return async ( ...args ) => selector.apply( null, args );
|
|
475
565
|
}
|
|
476
566
|
|
|
477
|
-
return ( ...args ) =>
|
|
478
|
-
|
|
479
|
-
const hasFinished = () =>
|
|
480
|
-
|
|
481
|
-
const finalize = ( result ) => {
|
|
482
|
-
const hasFailed = selectors.hasResolutionFailed(
|
|
567
|
+
return ( ...args ) =>
|
|
568
|
+
new Promise( ( resolve, reject ) => {
|
|
569
|
+
const hasFinished = () => {
|
|
570
|
+
return boundMetadataSelectors.hasFinishedResolution(
|
|
483
571
|
selectorName,
|
|
484
572
|
args
|
|
485
573
|
);
|
|
574
|
+
};
|
|
575
|
+
const finalize = ( result ) => {
|
|
576
|
+
const hasFailed =
|
|
577
|
+
boundMetadataSelectors.hasResolutionFailed(
|
|
578
|
+
selectorName,
|
|
579
|
+
args
|
|
580
|
+
);
|
|
486
581
|
if ( hasFailed ) {
|
|
487
|
-
const error =
|
|
582
|
+
const error = boundMetadataSelectors.getResolutionError(
|
|
488
583
|
selectorName,
|
|
489
584
|
args
|
|
490
585
|
);
|
|
@@ -494,6 +589,7 @@ function mapResolveSelectors( selectors, store ) {
|
|
|
494
589
|
}
|
|
495
590
|
};
|
|
496
591
|
const getResult = () => selector.apply( null, args );
|
|
592
|
+
|
|
497
593
|
// Trigger the selector (to trigger the resolver)
|
|
498
594
|
const result = getResult();
|
|
499
595
|
if ( hasFinished() ) {
|
|
@@ -507,20 +603,19 @@ function mapResolveSelectors( selectors, store ) {
|
|
|
507
603
|
}
|
|
508
604
|
} );
|
|
509
605
|
} );
|
|
510
|
-
|
|
511
|
-
} );
|
|
606
|
+
};
|
|
512
607
|
}
|
|
513
608
|
|
|
514
609
|
/**
|
|
515
610
|
* Maps selectors to functions that throw a suspense promise if not yet resolved.
|
|
516
611
|
*
|
|
517
|
-
* @param {Object} selectors
|
|
518
|
-
* @param {Object}
|
|
612
|
+
* @param {Object} store The redux store the selectors select from.
|
|
613
|
+
* @param {Object} boundMetadataSelectors The bound metadata selectors.
|
|
519
614
|
*
|
|
520
|
-
* @return {
|
|
615
|
+
* @return {Function} Function that maps selectors to their suspending versions.
|
|
521
616
|
*/
|
|
522
|
-
function
|
|
523
|
-
return
|
|
617
|
+
function mapSuspendSelector( store, boundMetadataSelectors ) {
|
|
618
|
+
return ( selector, selectorName ) => {
|
|
524
619
|
// Selector without a resolver doesn't have any extra suspense behavior.
|
|
525
620
|
if ( ! selector.hasResolver ) {
|
|
526
621
|
return selector;
|
|
@@ -529,9 +624,22 @@ function mapSuspendSelectors( selectors, store ) {
|
|
|
529
624
|
return ( ...args ) => {
|
|
530
625
|
const result = selector.apply( null, args );
|
|
531
626
|
|
|
532
|
-
if (
|
|
533
|
-
|
|
534
|
-
|
|
627
|
+
if (
|
|
628
|
+
boundMetadataSelectors.hasFinishedResolution(
|
|
629
|
+
selectorName,
|
|
630
|
+
args
|
|
631
|
+
)
|
|
632
|
+
) {
|
|
633
|
+
if (
|
|
634
|
+
boundMetadataSelectors.hasResolutionFailed(
|
|
635
|
+
selectorName,
|
|
636
|
+
args
|
|
637
|
+
)
|
|
638
|
+
) {
|
|
639
|
+
throw boundMetadataSelectors.getResolutionError(
|
|
640
|
+
selectorName,
|
|
641
|
+
args
|
|
642
|
+
);
|
|
535
643
|
}
|
|
536
644
|
|
|
537
645
|
return result;
|
|
@@ -540,7 +648,10 @@ function mapSuspendSelectors( selectors, store ) {
|
|
|
540
648
|
throw new Promise( ( resolve ) => {
|
|
541
649
|
const unsubscribe = store.subscribe( () => {
|
|
542
650
|
if (
|
|
543
|
-
|
|
651
|
+
boundMetadataSelectors.hasFinishedResolution(
|
|
652
|
+
selectorName,
|
|
653
|
+
args
|
|
654
|
+
)
|
|
544
655
|
) {
|
|
545
656
|
resolve();
|
|
546
657
|
unsubscribe();
|
|
@@ -548,26 +659,24 @@ function mapSuspendSelectors( selectors, store ) {
|
|
|
548
659
|
} );
|
|
549
660
|
} );
|
|
550
661
|
};
|
|
551
|
-
}
|
|
662
|
+
};
|
|
552
663
|
}
|
|
553
664
|
|
|
554
665
|
/**
|
|
555
|
-
* Convert
|
|
666
|
+
* Convert a resolver to a normalized form, an object with `fulfill` method and
|
|
556
667
|
* optional methods like `isFulfilled`.
|
|
557
668
|
*
|
|
558
|
-
* @param {
|
|
669
|
+
* @param {Function} resolver Resolver to convert
|
|
559
670
|
*/
|
|
560
|
-
function
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
671
|
+
function mapResolver( resolver ) {
|
|
672
|
+
if ( resolver.fulfill ) {
|
|
673
|
+
return resolver;
|
|
674
|
+
}
|
|
565
675
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
} );
|
|
676
|
+
return {
|
|
677
|
+
...resolver, // Copy the enumerable properties of the resolver function.
|
|
678
|
+
fulfill: resolver, // Add the fulfill method.
|
|
679
|
+
};
|
|
571
680
|
}
|
|
572
681
|
|
|
573
682
|
/**
|
|
@@ -575,18 +684,20 @@ function mapResolvers( resolvers ) {
|
|
|
575
684
|
* Resolvers are side effects invoked once per argument set of a given selector call,
|
|
576
685
|
* used in ensuring that the data needs for the selector are satisfied.
|
|
577
686
|
*
|
|
578
|
-
* @param {Object} selector
|
|
579
|
-
* @param {string} selectorName
|
|
580
|
-
* @param {Object} resolver
|
|
581
|
-
* @param {Object} store
|
|
582
|
-
* @param {Object} resolversCache
|
|
687
|
+
* @param {Object} selector The selector function to be bound.
|
|
688
|
+
* @param {string} selectorName The selector name.
|
|
689
|
+
* @param {Object} resolver Resolver to call.
|
|
690
|
+
* @param {Object} store The redux store to which the resolvers should be mapped.
|
|
691
|
+
* @param {Object} resolversCache Resolvers Cache.
|
|
692
|
+
* @param {Object} boundMetadataSelectors The bound metadata selectors.
|
|
583
693
|
*/
|
|
584
694
|
function mapSelectorWithResolver(
|
|
585
695
|
selector,
|
|
586
696
|
selectorName,
|
|
587
697
|
resolver,
|
|
588
698
|
store,
|
|
589
|
-
resolversCache
|
|
699
|
+
resolversCache,
|
|
700
|
+
boundMetadataSelectors
|
|
590
701
|
) {
|
|
591
702
|
function fulfillSelector( args ) {
|
|
592
703
|
const state = store.getState();
|
|
@@ -599,14 +710,8 @@ function mapSelectorWithResolver(
|
|
|
599
710
|
return;
|
|
600
711
|
}
|
|
601
712
|
|
|
602
|
-
const { metadata } = store.__unstableOriginalGetState();
|
|
603
|
-
|
|
604
713
|
if (
|
|
605
|
-
|
|
606
|
-
metadata,
|
|
607
|
-
selectorName,
|
|
608
|
-
args
|
|
609
|
-
)
|
|
714
|
+
boundMetadataSelectors.hasStartedResolution( selectorName, args )
|
|
610
715
|
) {
|
|
611
716
|
return;
|
|
612
717
|
}
|
|
@@ -309,10 +309,11 @@ describe( 'normalizing args', () => {
|
|
|
309
309
|
|
|
310
310
|
expect( normalizingFunction ).toHaveBeenCalledWith( [ 'foo', 'bar' ] );
|
|
311
311
|
|
|
312
|
-
// Needs to be called
|
|
312
|
+
// Needs to be called three times:
|
|
313
313
|
// 1. When the selector is called.
|
|
314
|
-
// 2. When the resolver
|
|
315
|
-
|
|
314
|
+
// 2. When the resolver check if it's already running.
|
|
315
|
+
// 3. When the resolver is fulfilled.
|
|
316
|
+
expect( normalizingFunction ).toHaveBeenCalledTimes( 3 );
|
|
316
317
|
} );
|
|
317
318
|
|
|
318
319
|
it( 'should not call the __unstableNormalizeArgs method if there are no arguments passed to the selector (and thus the resolver)', async () => {
|
package/src/test/privateAPIs.js
CHANGED
|
@@ -124,6 +124,52 @@ describe( 'Private data APIs', () => {
|
|
|
124
124
|
);
|
|
125
125
|
} );
|
|
126
126
|
|
|
127
|
+
it( 'should support private selectors with resolvers', async () => {
|
|
128
|
+
const testStore = createReduxStore( 'test', {
|
|
129
|
+
reducer: ( state = {}, action ) => {
|
|
130
|
+
if ( action.type === 'RECEIVE' ) {
|
|
131
|
+
return { ...state, [ action.key ]: action.value };
|
|
132
|
+
}
|
|
133
|
+
return state;
|
|
134
|
+
},
|
|
135
|
+
selectors: {},
|
|
136
|
+
resolvers: {
|
|
137
|
+
get:
|
|
138
|
+
( key ) =>
|
|
139
|
+
async ( { dispatch } ) => {
|
|
140
|
+
const value = await ( 'resolved-' + key );
|
|
141
|
+
dispatch( { type: 'RECEIVE', key, value } );
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
} );
|
|
145
|
+
registry.register( testStore );
|
|
146
|
+
unlock( testStore ).registerPrivateSelectors( {
|
|
147
|
+
get: ( state, key ) => state[ key ],
|
|
148
|
+
} );
|
|
149
|
+
|
|
150
|
+
// Verify that the private selector is available in resolveSelect.
|
|
151
|
+
const resolved = await unlock(
|
|
152
|
+
registry.resolveSelect( testStore )
|
|
153
|
+
).get( 'x' );
|
|
154
|
+
expect( resolved ).toBe( 'resolved-x' );
|
|
155
|
+
|
|
156
|
+
// Verify that the private selector is available in suspendSelect.
|
|
157
|
+
let suspended;
|
|
158
|
+
try {
|
|
159
|
+
unlock( registry.suspendSelect( testStore ) ).get( 'y' );
|
|
160
|
+
} catch ( error ) {
|
|
161
|
+
suspended = error;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
expect( suspended ).toBeInstanceOf( Promise );
|
|
165
|
+
await suspended;
|
|
166
|
+
|
|
167
|
+
// After the suspense promise resolves, the result should be ready.
|
|
168
|
+
expect(
|
|
169
|
+
unlock( registry.suspendSelect( testStore ) ).get( 'y' )
|
|
170
|
+
).toBe( 'resolved-y' );
|
|
171
|
+
} );
|
|
172
|
+
|
|
127
173
|
it( 'should give private selectors access to the state', () => {
|
|
128
174
|
const groceryStore = createStore();
|
|
129
175
|
unlock( groceryStore ).registerPrivateSelectors( {
|