react-native-onyx 3.0.18 → 3.0.20
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/dist/OnyxUtils.js +96 -6
- package/dist/useOnyx.js +6 -4
- package/package.json +1 -1
package/dist/OnyxUtils.js
CHANGED
|
@@ -465,6 +465,81 @@ function tryGetCachedValue(key) {
|
|
|
465
465
|
}
|
|
466
466
|
return val;
|
|
467
467
|
}
|
|
468
|
+
/**
|
|
469
|
+
* Marks items that existed in previousCollection but not in preservedCollection as null.
|
|
470
|
+
* This ensures subscribers are properly notified about item removals.
|
|
471
|
+
* @param preservedCollection - The collection to mark removed items in (mutated in place)
|
|
472
|
+
* @param previousCollection - The previous collection state to compare against
|
|
473
|
+
*/
|
|
474
|
+
function markRemovedItemsAsNull(preservedCollection, previousCollection) {
|
|
475
|
+
if (!previousCollection) {
|
|
476
|
+
return preservedCollection;
|
|
477
|
+
}
|
|
478
|
+
const mutablePreservedCollection = Object.assign({}, preservedCollection);
|
|
479
|
+
Object.keys(previousCollection).forEach((key) => {
|
|
480
|
+
if (key in preservedCollection) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
mutablePreservedCollection[key] = null;
|
|
484
|
+
});
|
|
485
|
+
return mutablePreservedCollection;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Utility function to preserve object references for unchanged items in collection operations.
|
|
489
|
+
* Compares new values with cached values using deep equality and preserves references when data is identical.
|
|
490
|
+
* @param keyValuePairs - Array of key-value pairs to process
|
|
491
|
+
* @param previousCollection - Optional previous collection state. If provided, removed items will be included as null
|
|
492
|
+
* @returns The preserved collection with unchanged references maintained and removed items marked as null
|
|
493
|
+
*/
|
|
494
|
+
function preserveCollectionReferences(keyValuePairs, previousCollection) {
|
|
495
|
+
const preservedCollection = {};
|
|
496
|
+
keyValuePairs.forEach(([key, value]) => {
|
|
497
|
+
const cachedValue = OnyxCache_1.default.get(key, false);
|
|
498
|
+
// If no cached value exists, we need to add the new value (skip expensive deep equality check)
|
|
499
|
+
// Use deep equality check to preserve references for unchanged items
|
|
500
|
+
if (cachedValue !== undefined && (0, fast_equals_1.deepEqual)(value, cachedValue)) {
|
|
501
|
+
// Keep the existing reference
|
|
502
|
+
preservedCollection[key] = cachedValue;
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
// Update cache only for changed items
|
|
506
|
+
OnyxCache_1.default.set(key, value);
|
|
507
|
+
preservedCollection[key] = value;
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
if (previousCollection) {
|
|
511
|
+
return markRemovedItemsAsNull(preservedCollection, previousCollection);
|
|
512
|
+
}
|
|
513
|
+
return preservedCollection;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Utility function for merge operations that preserves references after cache merge has been performed.
|
|
517
|
+
* Compares merged values with original cached values and preserves references when data is unchanged.
|
|
518
|
+
* @param collection - Collection of merged data
|
|
519
|
+
* @param originalCachedValues - Original cached values before merge
|
|
520
|
+
* @param previousCollection - Optional previous collection state. If provided, removed items will be included as null
|
|
521
|
+
* @returns The preserved collection with unchanged references maintained and removed items marked as null
|
|
522
|
+
*/
|
|
523
|
+
function preserveCollectionReferencesAfterMerge(collection, originalCachedValues, previousCollection) {
|
|
524
|
+
const preservedCollection = {};
|
|
525
|
+
Object.keys(collection).forEach((key) => {
|
|
526
|
+
const newMergedValue = OnyxCache_1.default.get(key, false);
|
|
527
|
+
const originalValue = originalCachedValues[key];
|
|
528
|
+
// Use deep equality check to preserve references for unchanged items
|
|
529
|
+
if (originalValue !== undefined && (0, fast_equals_1.deepEqual)(newMergedValue, originalValue)) {
|
|
530
|
+
// Keep the existing reference and update cache
|
|
531
|
+
preservedCollection[key] = originalValue;
|
|
532
|
+
OnyxCache_1.default.set(key, originalValue);
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
preservedCollection[key] = newMergedValue;
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
if (previousCollection) {
|
|
539
|
+
return markRemovedItemsAsNull(preservedCollection, previousCollection);
|
|
540
|
+
}
|
|
541
|
+
return preservedCollection;
|
|
542
|
+
}
|
|
468
543
|
function getCachedCollection(collectionKey, collectionMemberKeys) {
|
|
469
544
|
// Use optimized collection data retrieval when cache is populated
|
|
470
545
|
const collectionData = OnyxCache_1.default.getCollectionData(collectionKey);
|
|
@@ -1195,8 +1270,9 @@ function setCollectionWithRetry({ collectionKey, collection }, retryAttempt) {
|
|
|
1195
1270
|
});
|
|
1196
1271
|
const keyValuePairs = OnyxUtils.prepareKeyValuePairsForStorage(mutableCollection, true, undefined, true);
|
|
1197
1272
|
const previousCollection = OnyxUtils.getCachedCollection(collectionKey);
|
|
1198
|
-
|
|
1199
|
-
const
|
|
1273
|
+
// Preserve references for unchanged items and include removed items as null in setCollection
|
|
1274
|
+
const preservedCollection = preserveCollectionReferences(keyValuePairs, previousCollection);
|
|
1275
|
+
const updatePromise = OnyxUtils.scheduleNotifyCollectionSubscribers(collectionKey, preservedCollection, previousCollection);
|
|
1200
1276
|
return storage_1.default.multiSet(keyValuePairs)
|
|
1201
1277
|
.catch((error) => OnyxUtils.retryOperation(error, setCollectionWithRetry, { collectionKey, collection }, retryAttempt))
|
|
1202
1278
|
.then(() => {
|
|
@@ -1248,6 +1324,8 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
|
|
|
1248
1324
|
resultCollectionKeys = Object.keys(resultCollection);
|
|
1249
1325
|
return getAllKeys()
|
|
1250
1326
|
.then((persistedKeys) => {
|
|
1327
|
+
// Capture keys that will be removed (before calling remove())
|
|
1328
|
+
const keysToRemove = resultCollectionKeys.filter((key) => resultCollection[key] === null && persistedKeys.has(key));
|
|
1251
1329
|
// Split to keys that exist in storage and keys that don't
|
|
1252
1330
|
const keys = resultCollectionKeys.filter((key) => {
|
|
1253
1331
|
if (resultCollection[key] === null) {
|
|
@@ -1257,6 +1335,8 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
|
|
|
1257
1335
|
return true;
|
|
1258
1336
|
});
|
|
1259
1337
|
const existingKeys = keys.filter((key) => persistedKeys.has(key));
|
|
1338
|
+
// Get previous values for both existing keys and keys that will be removed
|
|
1339
|
+
const allAffectedKeys = [...existingKeys, ...keysToRemove];
|
|
1260
1340
|
const cachedCollectionForExistingKeys = getCachedCollection(collectionKey, existingKeys);
|
|
1261
1341
|
const existingKeyCollection = existingKeys.reduce((obj, key) => {
|
|
1262
1342
|
const { isCompatible, existingValueType, newValueType } = utils_1.default.checkCompatibilityWithExistingValue(resultCollection[key], cachedCollectionForExistingKeys[key]);
|
|
@@ -1285,7 +1365,8 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
|
|
|
1285
1365
|
const promises = [];
|
|
1286
1366
|
// We need to get the previously existing values so we can compare the new ones
|
|
1287
1367
|
// against them, to avoid unnecessary subscriber updates.
|
|
1288
|
-
|
|
1368
|
+
// Include keys that will be removed so subscribers are notified about removals
|
|
1369
|
+
const previousCollectionPromise = Promise.all(allAffectedKeys.map((key) => get(key).then((value) => [key, value]))).then(Object.fromEntries);
|
|
1289
1370
|
// New keys will be added via multiSet while existing keys will be updated using multiMerge
|
|
1290
1371
|
// This is because setting a key that doesn't exist yet with multiMerge will throw errors
|
|
1291
1372
|
if (keyValuePairsForExistingCollection.length > 0) {
|
|
@@ -1299,8 +1380,16 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
|
|
|
1299
1380
|
// Prefill cache if necessary by calling get() on any existing keys and then merge original data to cache
|
|
1300
1381
|
// and update all subscribers
|
|
1301
1382
|
const promiseUpdate = previousCollectionPromise.then((previousCollection) => {
|
|
1383
|
+
// Capture the original cached values before merging
|
|
1384
|
+
const originalCachedValues = {};
|
|
1385
|
+
Object.keys(finalMergedCollection).forEach((key) => {
|
|
1386
|
+
originalCachedValues[key] = OnyxCache_1.default.get(key, false);
|
|
1387
|
+
});
|
|
1388
|
+
// Then merge all the data into cache as normal
|
|
1302
1389
|
OnyxCache_1.default.merge(finalMergedCollection);
|
|
1303
|
-
|
|
1390
|
+
// Finally, preserve references for items that didn't actually change and include removed items as null
|
|
1391
|
+
const preservedCollection = preserveCollectionReferencesAfterMerge(finalMergedCollection, originalCachedValues, previousCollection);
|
|
1392
|
+
return scheduleNotifyCollectionSubscribers(collectionKey, preservedCollection, previousCollection);
|
|
1304
1393
|
});
|
|
1305
1394
|
return Promise.all(promises)
|
|
1306
1395
|
.catch((error) => retryOperation(error, mergeCollectionWithPatches, { collectionKey, collection: resultCollection, mergeReplaceNullPatches, isProcessingCollectionUpdate }, retryAttempt))
|
|
@@ -1351,8 +1440,9 @@ function partialSetCollection({ collectionKey, collection }, retryAttempt) {
|
|
|
1351
1440
|
const existingKeys = resultCollectionKeys.filter((key) => persistedKeys.has(key));
|
|
1352
1441
|
const previousCollection = getCachedCollection(collectionKey, existingKeys);
|
|
1353
1442
|
const keyValuePairs = prepareKeyValuePairsForStorage(mutableCollection, true, undefined, true);
|
|
1354
|
-
|
|
1355
|
-
const
|
|
1443
|
+
// Preserve references for unchanged items and include removed items as null in partialSetCollection
|
|
1444
|
+
const preservedCollection = preserveCollectionReferences(keyValuePairs, previousCollection);
|
|
1445
|
+
const updatePromise = scheduleNotifyCollectionSubscribers(collectionKey, preservedCollection, previousCollection);
|
|
1356
1446
|
return storage_1.default.multiSet(keyValuePairs)
|
|
1357
1447
|
.catch((error) => retryOperation(error, partialSetCollection, { collectionKey, collection }, retryAttempt))
|
|
1358
1448
|
.then(() => {
|
package/dist/useOnyx.js
CHANGED
|
@@ -234,22 +234,24 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
234
234
|
if (shouldUpdateResult) {
|
|
235
235
|
previousValueRef.current = newValueRef.current;
|
|
236
236
|
// If the new value is `null` we default it to `undefined` to ensure the consumer gets a consistent result from the hook.
|
|
237
|
-
|
|
237
|
+
newFetchStatus = newFetchStatus !== null && newFetchStatus !== void 0 ? newFetchStatus : 'loaded';
|
|
238
238
|
resultRef.current = [
|
|
239
239
|
(_d = previousValueRef.current) !== null && _d !== void 0 ? _d : undefined,
|
|
240
240
|
{
|
|
241
|
-
status:
|
|
241
|
+
status: newFetchStatus,
|
|
242
242
|
sourceValue: sourceValueRef.current,
|
|
243
243
|
},
|
|
244
244
|
];
|
|
245
245
|
// If `canBeMissing` is set to `false` and the Onyx value of that key is not defined,
|
|
246
246
|
// we log an alert so it can be acknowledged by the consumer. Additionally, we won't log alerts
|
|
247
247
|
// if there's a `Onyx.clear()` task in progress.
|
|
248
|
-
if ((options === null || options === void 0 ? void 0 : options.canBeMissing) === false &&
|
|
248
|
+
if ((options === null || options === void 0 ? void 0 : options.canBeMissing) === false && newFetchStatus === 'loaded' && !isOnyxValueDefined && !OnyxCache_1.default.hasPendingTask(OnyxCache_1.TASK.CLEAR)) {
|
|
249
249
|
Logger.logAlert(`useOnyx returned no data for key with canBeMissing set to false for key ${key}`, { showAlert: true });
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
|
-
|
|
252
|
+
if (newFetchStatus !== 'loading') {
|
|
253
|
+
OnyxSnapshotCache_1.default.setCachedResult(key, cacheKey, resultRef.current);
|
|
254
|
+
}
|
|
253
255
|
return resultRef.current;
|
|
254
256
|
}, [options === null || options === void 0 ? void 0 : options.initWithStoredValues, options === null || options === void 0 ? void 0 : options.allowStaleData, options === null || options === void 0 ? void 0 : options.canBeMissing, key, memoizedSelector, cacheKey]);
|
|
255
257
|
const subscribe = (0, react_1.useCallback)((onStoreChange) => {
|