react-native-onyx 3.0.9 → 3.0.10

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/Onyx.js CHANGED
@@ -626,8 +626,9 @@ function setCollection(collectionKey, collection) {
626
626
  });
627
627
  const keyValuePairs = OnyxUtils_1.default.prepareKeyValuePairsForStorage(mutableCollection, true, undefined, true);
628
628
  const previousCollection = OnyxUtils_1.default.getCachedCollection(collectionKey);
629
- keyValuePairs.forEach(([key, value]) => OnyxCache_1.default.set(key, value));
630
- const updatePromise = OnyxUtils_1.default.scheduleNotifyCollectionSubscribers(collectionKey, mutableCollection, previousCollection);
629
+ // Preserve references for unchanged items in setCollection
630
+ const preservedCollection = OnyxUtils_1.default.preserveCollectionReferences(keyValuePairs);
631
+ const updatePromise = OnyxUtils_1.default.scheduleNotifyCollectionSubscribers(collectionKey, preservedCollection, previousCollection);
631
632
  return storage_1.default.multiSet(keyValuePairs)
632
633
  .catch((error) => OnyxUtils_1.default.evictStorageAndRetry(error, setCollection, collectionKey, collection))
633
634
  .then(() => {
@@ -1,6 +1,6 @@
1
1
  import type { ValueOf } from 'type-fest';
2
2
  import type Onyx from './Onyx';
3
- import type { CollectionKey, CollectionKeyBase, ConnectOptions, DeepRecord, KeyValueMapping, CallbackToStateMapping, MultiMergeReplaceNullPatches, OnyxCollection, OnyxEntry, OnyxInput, OnyxKey, OnyxMergeCollectionInput, OnyxUpdate, OnyxValue, Selector } from './types';
3
+ import type { CollectionKey, CollectionKeyBase, ConnectOptions, DeepRecord, KeyValueMapping, CallbackToStateMapping, MultiMergeReplaceNullPatches, OnyxCollection, OnyxEntry, OnyxInput, OnyxInputKeyValueMapping, OnyxKey, OnyxMergeCollectionInput, OnyxUpdate, OnyxValue, Selector } from './types';
4
4
  import type { FastMergeResult } from './utils';
5
5
  import type { DeferredTask } from './createDeferredTask';
6
6
  import type { StorageKeyValuePair } from './storage/providers/types';
@@ -138,6 +138,18 @@ declare function getCollectionKey(key: CollectionKey): string;
138
138
  * If the requested key is a collection, it will return an object with all the collection members.
139
139
  */
140
140
  declare function tryGetCachedValue<TKey extends OnyxKey>(key: TKey): OnyxValue<OnyxKey>;
141
+ /**
142
+ * Utility function to preserve object references for unchanged items in collection operations.
143
+ * Compares new values with cached values using deep equality and preserves references when data is identical.
144
+ * @returns The preserved collection with unchanged references maintained
145
+ */
146
+ declare function preserveCollectionReferences(keyValuePairs: StorageKeyValuePair[]): OnyxInputKeyValueMapping;
147
+ /**
148
+ * Utility function for merge operations that preserves references after cache merge has been performed.
149
+ * Compares merged values with original cached values and preserves references when data is unchanged.
150
+ * @returns The preserved collection with unchanged references maintained
151
+ */
152
+ declare function preserveCollectionReferencesAfterMerge(collection: Record<string, OnyxValue<OnyxKey>>, originalCachedValues: Record<string, OnyxValue<OnyxKey>>): Record<string, OnyxValue<OnyxKey>>;
141
153
  declare function getCachedCollection<TKey extends CollectionKeyBase>(collectionKey: TKey, collectionMemberKeys?: string[]): NonNullable<OnyxCollection<KeyValueMapping[TKey]>>;
142
154
  /**
143
155
  * When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks
@@ -324,6 +336,8 @@ declare const OnyxUtils: {
324
336
  updateSnapshots: typeof updateSnapshots;
325
337
  mergeCollectionWithPatches: typeof mergeCollectionWithPatches;
326
338
  partialSetCollection: typeof partialSetCollection;
339
+ preserveCollectionReferences: typeof preserveCollectionReferences;
340
+ preserveCollectionReferencesAfterMerge: typeof preserveCollectionReferencesAfterMerge;
327
341
  logKeyChanged: typeof logKeyChanged;
328
342
  logKeyRemoved: typeof logKeyRemoved;
329
343
  };
package/dist/OnyxUtils.js CHANGED
@@ -452,6 +452,51 @@ function tryGetCachedValue(key) {
452
452
  }
453
453
  return val;
454
454
  }
455
+ /**
456
+ * Utility function to preserve object references for unchanged items in collection operations.
457
+ * Compares new values with cached values using deep equality and preserves references when data is identical.
458
+ * @returns The preserved collection with unchanged references maintained
459
+ */
460
+ function preserveCollectionReferences(keyValuePairs) {
461
+ const preservedCollection = {};
462
+ keyValuePairs.forEach(([key, value]) => {
463
+ const cachedValue = OnyxCache_1.default.get(key, false);
464
+ // If no cached value exists, we need to add the new value (skip expensive deep equality check)
465
+ // Use deep equality check to preserve references for unchanged items
466
+ if (cachedValue !== undefined && (0, fast_equals_1.deepEqual)(value, cachedValue)) {
467
+ // Keep the existing reference
468
+ preservedCollection[key] = cachedValue;
469
+ }
470
+ else {
471
+ // Update cache only for changed items
472
+ OnyxCache_1.default.set(key, value);
473
+ preservedCollection[key] = value;
474
+ }
475
+ });
476
+ return preservedCollection;
477
+ }
478
+ /**
479
+ * Utility function for merge operations that preserves references after cache merge has been performed.
480
+ * Compares merged values with original cached values and preserves references when data is unchanged.
481
+ * @returns The preserved collection with unchanged references maintained
482
+ */
483
+ function preserveCollectionReferencesAfterMerge(collection, originalCachedValues) {
484
+ const preservedCollection = {};
485
+ Object.keys(collection).forEach((key) => {
486
+ const newMergedValue = OnyxCache_1.default.get(key, false);
487
+ const originalValue = originalCachedValues[key];
488
+ // Use deep equality check to preserve references for unchanged items
489
+ if (originalValue !== undefined && (0, fast_equals_1.deepEqual)(newMergedValue, originalValue)) {
490
+ // Keep the existing reference and update cache
491
+ preservedCollection[key] = originalValue;
492
+ OnyxCache_1.default.set(key, originalValue);
493
+ }
494
+ else {
495
+ preservedCollection[key] = newMergedValue;
496
+ }
497
+ });
498
+ return preservedCollection;
499
+ }
455
500
  function getCachedCollection(collectionKey, collectionMemberKeys) {
456
501
  // Use optimized collection data retrieval when cache is populated
457
502
  const collectionData = OnyxCache_1.default.getCollectionData(collectionKey);
@@ -1094,10 +1139,18 @@ function mergeCollectionWithPatches(collectionKey, collection, mergeReplaceNullP
1094
1139
  // finalMergedCollection contains all the keys that were merged, without the keys of incompatible updates
1095
1140
  const finalMergedCollection = Object.assign(Object.assign({}, existingKeyCollection), newCollection);
1096
1141
  // Prefill cache if necessary by calling get() on any existing keys and then merge original data to cache
1097
- // and update all subscribers
1142
+ // and update all subscribers with reference preservation for unchanged items
1098
1143
  const promiseUpdate = previousCollectionPromise.then((previousCollection) => {
1144
+ // Capture the original cached values before merging
1145
+ const originalCachedValues = {};
1146
+ Object.keys(finalMergedCollection).forEach((key) => {
1147
+ originalCachedValues[key] = OnyxCache_1.default.get(key, false);
1148
+ });
1149
+ // Then merge all the data into cache as normal
1099
1150
  OnyxCache_1.default.merge(finalMergedCollection);
1100
- return scheduleNotifyCollectionSubscribers(collectionKey, finalMergedCollection, previousCollection);
1151
+ // Finally, preserve references for items that didn't actually change
1152
+ const preservedCollection = preserveCollectionReferencesAfterMerge(finalMergedCollection, originalCachedValues);
1153
+ return scheduleNotifyCollectionSubscribers(collectionKey, preservedCollection, previousCollection);
1101
1154
  });
1102
1155
  return Promise.all(promises)
1103
1156
  .catch((error) => evictStorageAndRetry(error, mergeCollectionWithPatches, collectionKey, resultCollection))
@@ -1145,8 +1198,9 @@ function partialSetCollection(collectionKey, collection) {
1145
1198
  const existingKeys = resultCollectionKeys.filter((key) => persistedKeys.has(key));
1146
1199
  const previousCollection = getCachedCollection(collectionKey, existingKeys);
1147
1200
  const keyValuePairs = prepareKeyValuePairsForStorage(mutableCollection, true, undefined, true);
1148
- keyValuePairs.forEach(([key, value]) => OnyxCache_1.default.set(key, value));
1149
- const updatePromise = scheduleNotifyCollectionSubscribers(collectionKey, mutableCollection, previousCollection);
1201
+ // Preserve references for unchanged items in partialSetCollection
1202
+ const preservedCollection = preserveCollectionReferences(keyValuePairs);
1203
+ const updatePromise = scheduleNotifyCollectionSubscribers(collectionKey, preservedCollection, previousCollection);
1150
1204
  return storage_1.default.multiSet(keyValuePairs)
1151
1205
  .catch((error) => evictStorageAndRetry(error, partialSetCollection, collectionKey, collection))
1152
1206
  .then(() => {
@@ -1223,6 +1277,8 @@ const OnyxUtils = {
1223
1277
  updateSnapshots,
1224
1278
  mergeCollectionWithPatches,
1225
1279
  partialSetCollection,
1280
+ preserveCollectionReferences,
1281
+ preserveCollectionReferencesAfterMerge,
1226
1282
  logKeyChanged,
1227
1283
  logKeyRemoved,
1228
1284
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.9",
3
+ "version": "3.0.10",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",