react-native-onyx 3.0.20 → 3.0.22

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.d.ts CHANGED
@@ -139,7 +139,7 @@ declare function clear(keysToPreserve?: OnyxKey[]): Promise<void>;
139
139
  * @param data An array of objects with update expressions
140
140
  * @returns resolves when all operations are complete
141
141
  */
142
- declare function update(data: OnyxUpdate[]): Promise<void>;
142
+ declare function update<TKey extends OnyxKey>(data: Array<OnyxUpdate<TKey>>): Promise<void>;
143
143
  /**
144
144
  * Sets a collection by replacing all existing collection members with new values.
145
145
  * Any existing collection members not included in the new data will be removed.
@@ -241,7 +241,7 @@ declare function subscribeToKey<TKey extends OnyxKey>(connectOptions: ConnectOpt
241
241
  * @param subscriptionID Subscription ID returned by calling `OnyxUtils.subscribeToKey()`.
242
242
  */
243
243
  declare function unsubscribeFromKey(subscriptionID: number): void;
244
- declare function updateSnapshots(data: OnyxUpdate[], mergeFn: typeof Onyx.merge): Array<() => Promise<void>>;
244
+ declare function updateSnapshots<TKey extends OnyxKey>(data: Array<OnyxUpdate<TKey>>, mergeFn: typeof Onyx.merge): Array<() => Promise<void>>;
245
245
  /**
246
246
  * Writes a value to our store with the given key.
247
247
  * Serves as core implementation for `Onyx.set()` public function, the difference being
package/dist/OnyxUtils.js CHANGED
@@ -465,81 +465,6 @@ 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
- }
543
468
  function getCachedCollection(collectionKey, collectionMemberKeys) {
544
469
  // Use optimized collection data retrieval when cache is populated
545
470
  const collectionData = OnyxCache_1.default.getCollectionData(collectionKey);
@@ -1270,9 +1195,8 @@ function setCollectionWithRetry({ collectionKey, collection }, retryAttempt) {
1270
1195
  });
1271
1196
  const keyValuePairs = OnyxUtils.prepareKeyValuePairsForStorage(mutableCollection, true, undefined, true);
1272
1197
  const previousCollection = OnyxUtils.getCachedCollection(collectionKey);
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);
1198
+ keyValuePairs.forEach(([key, value]) => OnyxCache_1.default.set(key, value));
1199
+ const updatePromise = OnyxUtils.scheduleNotifyCollectionSubscribers(collectionKey, mutableCollection, previousCollection);
1276
1200
  return storage_1.default.multiSet(keyValuePairs)
1277
1201
  .catch((error) => OnyxUtils.retryOperation(error, setCollectionWithRetry, { collectionKey, collection }, retryAttempt))
1278
1202
  .then(() => {
@@ -1324,8 +1248,6 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
1324
1248
  resultCollectionKeys = Object.keys(resultCollection);
1325
1249
  return getAllKeys()
1326
1250
  .then((persistedKeys) => {
1327
- // Capture keys that will be removed (before calling remove())
1328
- const keysToRemove = resultCollectionKeys.filter((key) => resultCollection[key] === null && persistedKeys.has(key));
1329
1251
  // Split to keys that exist in storage and keys that don't
1330
1252
  const keys = resultCollectionKeys.filter((key) => {
1331
1253
  if (resultCollection[key] === null) {
@@ -1335,8 +1257,6 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
1335
1257
  return true;
1336
1258
  });
1337
1259
  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];
1340
1260
  const cachedCollectionForExistingKeys = getCachedCollection(collectionKey, existingKeys);
1341
1261
  const existingKeyCollection = existingKeys.reduce((obj, key) => {
1342
1262
  const { isCompatible, existingValueType, newValueType } = utils_1.default.checkCompatibilityWithExistingValue(resultCollection[key], cachedCollectionForExistingKeys[key]);
@@ -1365,8 +1285,7 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
1365
1285
  const promises = [];
1366
1286
  // We need to get the previously existing values so we can compare the new ones
1367
1287
  // against them, to avoid unnecessary subscriber updates.
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);
1288
+ const previousCollectionPromise = Promise.all(existingKeys.map((key) => get(key).then((value) => [key, value]))).then(Object.fromEntries);
1370
1289
  // New keys will be added via multiSet while existing keys will be updated using multiMerge
1371
1290
  // This is because setting a key that doesn't exist yet with multiMerge will throw errors
1372
1291
  if (keyValuePairsForExistingCollection.length > 0) {
@@ -1380,16 +1299,8 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
1380
1299
  // Prefill cache if necessary by calling get() on any existing keys and then merge original data to cache
1381
1300
  // and update all subscribers
1382
1301
  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
1389
1302
  OnyxCache_1.default.merge(finalMergedCollection);
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);
1303
+ return scheduleNotifyCollectionSubscribers(collectionKey, finalMergedCollection, previousCollection);
1393
1304
  });
1394
1305
  return Promise.all(promises)
1395
1306
  .catch((error) => retryOperation(error, mergeCollectionWithPatches, { collectionKey, collection: resultCollection, mergeReplaceNullPatches, isProcessingCollectionUpdate }, retryAttempt))
@@ -1440,9 +1351,8 @@ function partialSetCollection({ collectionKey, collection }, retryAttempt) {
1440
1351
  const existingKeys = resultCollectionKeys.filter((key) => persistedKeys.has(key));
1441
1352
  const previousCollection = getCachedCollection(collectionKey, existingKeys);
1442
1353
  const keyValuePairs = prepareKeyValuePairsForStorage(mutableCollection, true, undefined, true);
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);
1354
+ keyValuePairs.forEach(([key, value]) => OnyxCache_1.default.set(key, value));
1355
+ const updatePromise = scheduleNotifyCollectionSubscribers(collectionKey, mutableCollection, previousCollection);
1446
1356
  return storage_1.default.multiSet(keyValuePairs)
1447
1357
  .catch((error) => retryOperation(error, partialSetCollection, { collectionKey, collection }, retryAttempt))
1448
1358
  .then(() => {
package/dist/types.d.ts CHANGED
@@ -175,9 +175,7 @@ type NullishObjectDeep<ObjectType extends object> = {
175
175
  * Also, the `TMap` type is inferred automatically in `mergeCollection()` method and represents
176
176
  * the object of collection keys/values specified in the second parameter of the method.
177
177
  */
178
- type Collection<TKey extends CollectionKeyBase, TValue> = Record<`${TKey}${string}`, TValue> & {
179
- [P in TKey]?: never;
180
- };
178
+ type Collection<TKey extends CollectionKeyBase, TValue> = Record<`${TKey}${string}`, TValue>;
181
179
  /** Represents the base options used in `Onyx.connect()` method. */
182
180
  type BaseConnectOptions = {
183
181
  /** If set to `false`, then the initial data will be only sent to the callback function if it changes. */
@@ -270,40 +268,39 @@ type OnyxMergeCollectionInput<TKey extends OnyxKey> = Collection<TKey, NonNullab
270
268
  */
271
269
  type OnyxSetCollectionInput<TKey extends OnyxKey> = Collection<TKey, OnyxInput<TKey>>;
272
270
  type OnyxMethodMap = typeof OnyxUtils.METHOD;
271
+ type ExpandOnyxKeys<TKey extends OnyxKey> = TKey extends CollectionKeyBase ? NoInfer<`${TKey}${string}`> : TKey;
273
272
  /**
274
273
  * OnyxUpdate type includes all onyx methods used in OnyxMethodValueMap.
275
274
  * If a new method is added to OnyxUtils.METHOD constant, it must be added to OnyxMethodValueMap type.
276
275
  * Otherwise it will show static type errors.
277
276
  */
278
- type OnyxUpdate = {
279
- [TKey in OnyxKey]: {
277
+ type OnyxUpdate<TKey extends OnyxKey = OnyxKey> = {
278
+ [K in TKey]: {
280
279
  onyxMethod: typeof OnyxUtils.METHOD.SET;
281
- key: TKey;
282
- value: OnyxSetInput<TKey>;
280
+ key: ExpandOnyxKeys<K>;
281
+ value: OnyxSetInput<K>;
283
282
  } | {
284
283
  onyxMethod: typeof OnyxUtils.METHOD.MULTI_SET;
285
- key: TKey;
284
+ key: ExpandOnyxKeys<K>;
286
285
  value: OnyxMultiSetInput;
287
286
  } | {
288
287
  onyxMethod: typeof OnyxUtils.METHOD.MERGE;
289
- key: TKey;
290
- value: OnyxMergeInput<TKey>;
288
+ key: ExpandOnyxKeys<K>;
289
+ value: OnyxMergeInput<K>;
291
290
  } | {
292
291
  onyxMethod: typeof OnyxUtils.METHOD.CLEAR;
293
- key: TKey;
294
- value?: undefined;
295
- };
296
- }[OnyxKey] | {
297
- [TKey in CollectionKeyBase]: {
292
+ key: ExpandOnyxKeys<K>;
293
+ value?: never;
294
+ } | {
298
295
  onyxMethod: typeof OnyxUtils.METHOD.MERGE_COLLECTION;
299
- key: TKey;
300
- value: OnyxMergeCollectionInput<TKey>;
296
+ key: K;
297
+ value: OnyxMergeCollectionInput<K>;
301
298
  } | {
302
299
  onyxMethod: typeof OnyxUtils.METHOD.SET_COLLECTION;
303
- key: TKey;
304
- value: OnyxSetCollectionInput<TKey>;
300
+ key: K;
301
+ value: OnyxSetCollectionInput<K>;
305
302
  };
306
- }[CollectionKeyBase];
303
+ }[TKey];
307
304
  /**
308
305
  * Represents the options used in `Onyx.set()` method.
309
306
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.20",
3
+ "version": "3.0.22",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",