@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.
- package/CHANGELOG.md +6 -0
- package/README.md +28 -0
- package/build/components/use-select/index.js +148 -36
- package/build/components/use-select/index.js.map +1 -1
- package/build/index.js +22 -3
- package/build/index.js.map +1 -1
- package/build/plugins/persistence/index.js +2 -375
- package/build/plugins/persistence/index.js.map +1 -1
- package/build/redux-store/index.js +52 -5
- package/build/redux-store/index.js.map +1 -1
- package/build/registry.js +31 -4
- package/build/registry.js.map +1 -1
- package/build-module/components/use-select/index.js +146 -36
- package/build-module/components/use-select/index.js.map +1 -1
- package/build-module/index.js +13 -1
- package/build-module/index.js.map +1 -1
- package/build-module/plugins/persistence/index.js +2 -365
- package/build-module/plugins/persistence/index.js.map +1 -1
- package/build-module/redux-store/index.js +52 -5
- package/build-module/redux-store/index.js.map +1 -1
- package/build-module/registry.js +31 -4
- package/build-module/registry.js.map +1 -1
- package/package.json +8 -8
- package/src/components/use-select/index.js +163 -33
- package/src/components/use-select/test/index.js +574 -264
- package/src/components/use-select/test/suspense.js +160 -0
- package/src/index.js +16 -1
- package/src/plugins/persistence/index.js +2 -420
- package/src/plugins/persistence/test/index.js +1 -777
- package/src/redux-store/index.js +43 -0
- package/src/registry.js +30 -4
- package/src/test/registry.js +8 -1
- package/tsconfig.json +2 -2
|
@@ -112,17 +112,12 @@ export default function useSelect( mapSelect, deps ) {
|
|
|
112
112
|
|
|
113
113
|
const registry = useRegistry();
|
|
114
114
|
const isAsync = useAsyncMode();
|
|
115
|
-
// React can sometimes clear the `useMemo` cache.
|
|
116
|
-
// We use the cache-stable `useMemoOne` to avoid
|
|
117
|
-
// losing queues.
|
|
118
|
-
const queueContext = useMemoOne( () => ( { queue: true } ), [ registry ] );
|
|
119
|
-
const [ , forceRender ] = useReducer( ( s ) => s + 1, 0 );
|
|
120
115
|
|
|
116
|
+
const latestRegistry = useRef( registry );
|
|
121
117
|
const latestMapSelect = useRef();
|
|
122
118
|
const latestIsAsync = useRef( isAsync );
|
|
123
119
|
const latestMapOutput = useRef();
|
|
124
120
|
const latestMapOutputError = useRef();
|
|
125
|
-
const isMountedAndNotUnsubscribing = useRef();
|
|
126
121
|
|
|
127
122
|
// Keep track of the stores being selected in the _mapSelect function,
|
|
128
123
|
// and only subscribe to those stores later.
|
|
@@ -145,10 +140,17 @@ export default function useSelect( mapSelect, deps ) {
|
|
|
145
140
|
|
|
146
141
|
if ( _mapSelect ) {
|
|
147
142
|
mapOutput = latestMapOutput.current;
|
|
143
|
+
const hasReplacedRegistry = latestRegistry.current !== registry;
|
|
148
144
|
const hasReplacedMapSelect = latestMapSelect.current !== _mapSelect;
|
|
145
|
+
const hasLeftAsyncMode = latestIsAsync.current && ! isAsync;
|
|
149
146
|
const lastMapSelectFailed = !! latestMapOutputError.current;
|
|
150
147
|
|
|
151
|
-
if (
|
|
148
|
+
if (
|
|
149
|
+
hasReplacedRegistry ||
|
|
150
|
+
hasReplacedMapSelect ||
|
|
151
|
+
hasLeftAsyncMode ||
|
|
152
|
+
lastMapSelectFailed
|
|
153
|
+
) {
|
|
152
154
|
try {
|
|
153
155
|
mapOutput = wrapSelect( _mapSelect );
|
|
154
156
|
} catch ( error ) {
|
|
@@ -171,45 +173,44 @@ export default function useSelect( mapSelect, deps ) {
|
|
|
171
173
|
return;
|
|
172
174
|
}
|
|
173
175
|
|
|
176
|
+
latestRegistry.current = registry;
|
|
174
177
|
latestMapSelect.current = _mapSelect;
|
|
178
|
+
latestIsAsync.current = isAsync;
|
|
175
179
|
latestMapOutput.current = mapOutput;
|
|
176
180
|
latestMapOutputError.current = undefined;
|
|
177
|
-
isMountedAndNotUnsubscribing.current = true;
|
|
178
|
-
|
|
179
|
-
// This has to run after the other ref updates
|
|
180
|
-
// to avoid using stale values in the flushed
|
|
181
|
-
// callbacks or potentially overwriting a
|
|
182
|
-
// changed `latestMapOutput.current`.
|
|
183
|
-
if ( latestIsAsync.current !== isAsync ) {
|
|
184
|
-
latestIsAsync.current = isAsync;
|
|
185
|
-
renderQueue.flush( queueContext );
|
|
186
|
-
}
|
|
187
181
|
} );
|
|
188
182
|
|
|
183
|
+
// React can sometimes clear the `useMemo` cache.
|
|
184
|
+
// We use the cache-stable `useMemoOne` to avoid
|
|
185
|
+
// losing queues.
|
|
186
|
+
const queueContext = useMemoOne( () => ( { queue: true } ), [ registry ] );
|
|
187
|
+
const [ , forceRender ] = useReducer( ( s ) => s + 1, 0 );
|
|
188
|
+
const isMounted = useRef( false );
|
|
189
|
+
|
|
189
190
|
useIsomorphicLayoutEffect( () => {
|
|
190
191
|
if ( ! hasMappingFunction ) {
|
|
191
192
|
return;
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
const onStoreChange = () => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
isShallowEqual( latestMapOutput.current, newMapOutput )
|
|
201
|
-
) {
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
latestMapOutput.current = newMapOutput;
|
|
205
|
-
} catch ( error ) {
|
|
206
|
-
latestMapOutputError.current = error;
|
|
196
|
+
try {
|
|
197
|
+
const newMapOutput = wrapSelect( latestMapSelect.current );
|
|
198
|
+
|
|
199
|
+
if ( isShallowEqual( latestMapOutput.current, newMapOutput ) ) {
|
|
200
|
+
return;
|
|
207
201
|
}
|
|
208
|
-
|
|
202
|
+
latestMapOutput.current = newMapOutput;
|
|
203
|
+
} catch ( error ) {
|
|
204
|
+
latestMapOutputError.current = error;
|
|
209
205
|
}
|
|
206
|
+
forceRender();
|
|
210
207
|
};
|
|
211
208
|
|
|
212
209
|
const onChange = () => {
|
|
210
|
+
if ( ! isMounted.current ) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
213
214
|
if ( latestIsAsync.current ) {
|
|
214
215
|
renderQueue.add( queueContext, onStoreChange );
|
|
215
216
|
} else {
|
|
@@ -219,17 +220,19 @@ export default function useSelect( mapSelect, deps ) {
|
|
|
219
220
|
|
|
220
221
|
// Catch any possible state changes during mount before the subscription
|
|
221
222
|
// could be set.
|
|
222
|
-
|
|
223
|
+
onStoreChange();
|
|
223
224
|
|
|
224
225
|
const unsubscribers = listeningStores.current.map( ( storeName ) =>
|
|
225
226
|
registry.__unstableSubscribeStore( storeName, onChange )
|
|
226
227
|
);
|
|
227
228
|
|
|
229
|
+
isMounted.current = true;
|
|
230
|
+
|
|
228
231
|
return () => {
|
|
229
|
-
isMountedAndNotUnsubscribing.current = false;
|
|
230
232
|
// The return value of the subscribe function could be undefined if the store is a custom generic store.
|
|
231
233
|
unsubscribers.forEach( ( unsubscribe ) => unsubscribe?.() );
|
|
232
|
-
renderQueue.
|
|
234
|
+
renderQueue.cancel( queueContext );
|
|
235
|
+
isMounted.current = false;
|
|
233
236
|
};
|
|
234
237
|
// If you're tempted to eliminate the spread dependencies below don't do it!
|
|
235
238
|
// We're passing these in from the calling function and want to make sure we're
|
|
@@ -238,3 +241,130 @@ export default function useSelect( mapSelect, deps ) {
|
|
|
238
241
|
|
|
239
242
|
return hasMappingFunction ? mapOutput : registry.select( mapSelect );
|
|
240
243
|
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* A variant of the `useSelect` hook that has the same API, but will throw a
|
|
247
|
+
* suspense Promise if any of the called selectors is in an unresolved state.
|
|
248
|
+
*
|
|
249
|
+
* @param {Function} mapSelect Function called on every state change. The
|
|
250
|
+
* returned value is exposed to the component
|
|
251
|
+
* using this hook. The function receives the
|
|
252
|
+
* `registry.suspendSelect` method as the first
|
|
253
|
+
* argument and the `registry` as the second one.
|
|
254
|
+
* @param {Array} deps A dependency array used to memoize the `mapSelect`
|
|
255
|
+
* so that the same `mapSelect` is invoked on every
|
|
256
|
+
* state change unless the dependencies change.
|
|
257
|
+
*
|
|
258
|
+
* @return {Object} Data object returned by the `mapSelect` function.
|
|
259
|
+
*/
|
|
260
|
+
export function useSuspenseSelect( mapSelect, deps ) {
|
|
261
|
+
const _mapSelect = useCallback( mapSelect, deps );
|
|
262
|
+
|
|
263
|
+
const registry = useRegistry();
|
|
264
|
+
const isAsync = useAsyncMode();
|
|
265
|
+
|
|
266
|
+
const latestRegistry = useRef( registry );
|
|
267
|
+
const latestMapSelect = useRef();
|
|
268
|
+
const latestIsAsync = useRef( isAsync );
|
|
269
|
+
const latestMapOutput = useRef();
|
|
270
|
+
const latestMapOutputError = useRef();
|
|
271
|
+
|
|
272
|
+
// Keep track of the stores being selected in the `mapSelect` function,
|
|
273
|
+
// and only subscribe to those stores later.
|
|
274
|
+
const listeningStores = useRef( [] );
|
|
275
|
+
const wrapSelect = useCallback(
|
|
276
|
+
( callback ) =>
|
|
277
|
+
registry.__unstableMarkListeningStores(
|
|
278
|
+
() => callback( registry.suspendSelect, registry ),
|
|
279
|
+
listeningStores
|
|
280
|
+
),
|
|
281
|
+
[ registry ]
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// Generate a "flag" for used in the effect dependency array.
|
|
285
|
+
// It's different than just using `mapSelect` since deps could be undefined,
|
|
286
|
+
// in that case, we would still want to memoize it.
|
|
287
|
+
const depsChangedFlag = useMemo( () => ( {} ), deps || [] );
|
|
288
|
+
|
|
289
|
+
let mapOutput = latestMapOutput.current;
|
|
290
|
+
let mapOutputError = latestMapOutputError.current;
|
|
291
|
+
|
|
292
|
+
const hasReplacedRegistry = latestRegistry.current !== registry;
|
|
293
|
+
const hasReplacedMapSelect = latestMapSelect.current !== _mapSelect;
|
|
294
|
+
const hasLeftAsyncMode = latestIsAsync.current && ! isAsync;
|
|
295
|
+
|
|
296
|
+
if ( hasReplacedRegistry || hasReplacedMapSelect || hasLeftAsyncMode ) {
|
|
297
|
+
try {
|
|
298
|
+
mapOutput = wrapSelect( _mapSelect );
|
|
299
|
+
} catch ( error ) {
|
|
300
|
+
mapOutputError = error;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
useIsomorphicLayoutEffect( () => {
|
|
305
|
+
latestRegistry.current = registry;
|
|
306
|
+
latestMapSelect.current = _mapSelect;
|
|
307
|
+
latestIsAsync.current = isAsync;
|
|
308
|
+
latestMapOutput.current = mapOutput;
|
|
309
|
+
latestMapOutputError.current = mapOutputError;
|
|
310
|
+
} );
|
|
311
|
+
|
|
312
|
+
// React can sometimes clear the `useMemo` cache.
|
|
313
|
+
// We use the cache-stable `useMemoOne` to avoid
|
|
314
|
+
// losing queues.
|
|
315
|
+
const queueContext = useMemoOne( () => ( { queue: true } ), [ registry ] );
|
|
316
|
+
const [ , forceRender ] = useReducer( ( s ) => s + 1, 0 );
|
|
317
|
+
const isMounted = useRef( false );
|
|
318
|
+
|
|
319
|
+
useIsomorphicLayoutEffect( () => {
|
|
320
|
+
const onStoreChange = () => {
|
|
321
|
+
try {
|
|
322
|
+
const newMapOutput = wrapSelect( latestMapSelect.current );
|
|
323
|
+
|
|
324
|
+
if ( isShallowEqual( latestMapOutput.current, newMapOutput ) ) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
latestMapOutput.current = newMapOutput;
|
|
328
|
+
} catch ( error ) {
|
|
329
|
+
latestMapOutputError.current = error;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
forceRender();
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const onChange = () => {
|
|
336
|
+
if ( ! isMounted.current ) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if ( latestIsAsync.current ) {
|
|
341
|
+
renderQueue.add( queueContext, onStoreChange );
|
|
342
|
+
} else {
|
|
343
|
+
onStoreChange();
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// catch any possible state changes during mount before the subscription
|
|
348
|
+
// could be set.
|
|
349
|
+
onStoreChange();
|
|
350
|
+
|
|
351
|
+
const unsubscribers = listeningStores.current.map( ( storeName ) =>
|
|
352
|
+
registry.__unstableSubscribeStore( storeName, onChange )
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
isMounted.current = true;
|
|
356
|
+
|
|
357
|
+
return () => {
|
|
358
|
+
// The return value of the subscribe function could be undefined if the store is a custom generic store.
|
|
359
|
+
unsubscribers.forEach( ( unsubscribe ) => unsubscribe?.() );
|
|
360
|
+
renderQueue.cancel( queueContext );
|
|
361
|
+
isMounted.current = false;
|
|
362
|
+
};
|
|
363
|
+
}, [ registry, wrapSelect, depsChangedFlag ] );
|
|
364
|
+
|
|
365
|
+
if ( mapOutputError ) {
|
|
366
|
+
throw mapOutputError;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return mapOutput;
|
|
370
|
+
}
|