react-native-onyx 3.0.60 → 3.0.62

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
@@ -246,9 +246,8 @@ function merge(key, changes) {
246
246
  OnyxUtils_1.default.logKeyRemoved(OnyxUtils_1.default.METHOD.MERGE, key);
247
247
  return Promise.resolve();
248
248
  }
249
- return OnyxMerge_1.default.applyMerge(key, existingValue, validChanges).then(({ mergedValue, updatePromise }) => {
249
+ return OnyxMerge_1.default.applyMerge(key, existingValue, validChanges).then(({ mergedValue }) => {
250
250
  OnyxUtils_1.default.sendActionToDevTools(OnyxUtils_1.default.METHOD.MERGE, key, changes, mergedValue);
251
- return updatePromise;
252
251
  });
253
252
  }
254
253
  catch (error) {
@@ -347,14 +346,6 @@ function clear(keysToPreserve = []) {
347
346
  // If it isn't preserved and doesn't have a default, we'll remove it
348
347
  keysToBeClearedFromStorage.push(key);
349
348
  }
350
- const updatePromises = [];
351
- // Notify the subscribers for each key/value group so they can receive the new values
352
- for (const [key, value] of Object.entries(keyValuesToResetIndividually)) {
353
- updatePromises.push(OnyxUtils_1.default.scheduleSubscriberUpdate(key, value));
354
- }
355
- for (const [key, value] of Object.entries(keyValuesToResetAsCollection)) {
356
- updatePromises.push(OnyxUtils_1.default.scheduleNotifyCollectionSubscribers(key, value.newValues, value.oldValues));
357
- }
358
349
  // Exclude RAM-only keys to prevent them from being saved to storage
359
350
  const defaultKeyValuePairs = Object.entries(Object.keys(defaultKeyStates)
360
351
  .filter((key) => !keysToPreserve.some((preserveKey) => OnyxKeys_1.default.isKeyMatch(preserveKey, key)) && !OnyxKeys_1.default.isRamOnlyKey(key))
@@ -371,7 +362,13 @@ function clear(keysToPreserve = []) {
371
362
  .then(() => storage_1.default.multiSet(defaultKeyValuePairs))
372
363
  .then(() => {
373
364
  DevTools_1.default.clearState(keysToPreserve);
374
- return Promise.all(updatePromises);
365
+ // Notify the subscribers for each key/value group so they can receive the new values
366
+ for (const [key, value] of Object.entries(keyValuesToResetIndividually)) {
367
+ OnyxUtils_1.default.keyChanged(key, value);
368
+ }
369
+ for (const [key, value] of Object.entries(keyValuesToResetAsCollection)) {
370
+ OnyxUtils_1.default.keysChanged(key, value.newValues, value.oldValues);
371
+ }
375
372
  });
376
373
  })
377
374
  .then(() => undefined);
@@ -14,17 +14,16 @@ const applyMerge = (key, existingValue, validChanges) => {
14
14
  // Logging properties only since values could be sensitive things we don't want to log.
15
15
  OnyxUtils_1.default.logKeyChanged(OnyxUtils_1.default.METHOD.MERGE, key, mergedValue, hasChanged);
16
16
  // This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
17
- const updatePromise = OnyxUtils_1.default.broadcastUpdate(key, mergedValue, hasChanged);
17
+ OnyxUtils_1.default.broadcastUpdate(key, mergedValue, hasChanged);
18
18
  const shouldSkipStorageOperations = !hasChanged || OnyxKeys_1.default.isRamOnlyKey(key);
19
19
  // If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
20
20
  // If the key is marked as RAM-only, it should not be saved nor updated in the storage.
21
21
  if (shouldSkipStorageOperations) {
22
- return Promise.resolve({ mergedValue, updatePromise });
22
+ return Promise.resolve({ mergedValue });
23
23
  }
24
24
  // For web platforms we use `setItem` since the object was already merged with its changes before.
25
25
  return storage_1.default.setItem(key, mergedValue).then(() => ({
26
26
  mergedValue,
27
- updatePromise,
28
27
  }));
29
28
  };
30
29
  const OnyxMerge = {
@@ -20,18 +20,17 @@ const applyMerge = (key, existingValue, validChanges) => {
20
20
  // Logging properties only since values could be sensitive things we don't want to log.
21
21
  OnyxUtils_1.default.logKeyChanged(OnyxUtils_1.default.METHOD.MERGE, key, mergedValue, hasChanged);
22
22
  // This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
23
- const updatePromise = OnyxUtils_1.default.broadcastUpdate(key, mergedValue, hasChanged);
23
+ OnyxUtils_1.default.broadcastUpdate(key, mergedValue, hasChanged);
24
24
  const shouldSkipStorageOperations = !hasChanged || OnyxKeys_1.default.isRamOnlyKey(key);
25
25
  // If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
26
26
  // If the key is marked as RAM-only, it should not be saved nor updated in the storage.
27
27
  if (shouldSkipStorageOperations) {
28
- return Promise.resolve({ mergedValue, updatePromise });
28
+ return Promise.resolve({ mergedValue });
29
29
  }
30
30
  // For native platforms we use `mergeItem` that will take advantage of JSON_PATCH and JSON_REPLACE SQL operations to
31
31
  // merge the object in a performant way.
32
32
  return storage_1.default.mergeItem(key, batchedChanges, replaceNullPatches).then(() => ({
33
33
  mergedValue,
34
- updatePromise,
35
34
  }));
36
35
  };
37
36
  const OnyxMerge = {
@@ -1,7 +1,6 @@
1
1
  import type { OnyxInput, OnyxKey } from '../types';
2
2
  type ApplyMergeResult<TValue> = {
3
3
  mergedValue: TValue;
4
- updatePromise: Promise<void>;
5
4
  };
6
5
  type ApplyMerge = <TKey extends OnyxKey, TValue extends OnyxInput<OnyxKey> | undefined, TChange extends OnyxInput<OnyxKey> | null>(key: TKey, existingValue: TValue, validChanges: TChange[]) => Promise<ApplyMergeResult<TChange>>;
7
6
  export type { ApplyMerge, ApplyMergeResult };
@@ -126,7 +126,7 @@ declare function keyChanged<TKey extends OnyxKey>(key: TKey, value: OnyxValue<TK
126
126
  /**
127
127
  * Sends the data obtained from the keys to the connection.
128
128
  */
129
- declare function sendDataToConnection<TKey extends OnyxKey>(mapping: CallbackToStateMapping<TKey>, value: OnyxValue<TKey> | null, matchedKey: TKey | undefined): void;
129
+ declare function sendDataToConnection<TKey extends OnyxKey>(mapping: CallbackToStateMapping<TKey>, matchedKey: TKey | undefined): void;
130
130
  /**
131
131
  * We check to see if this key is flagged as safe for eviction and add it to the recentlyAccessedKeys list so that when we
132
132
  * run out of storage the least recently accessed key can be removed.
@@ -136,19 +136,6 @@ declare function addKeyToRecentlyAccessedIfNeeded<TKey extends OnyxKey>(key: TKe
136
136
  * Gets the data for a given an array of matching keys, combines them into an object, and sends the result back to the subscriber.
137
137
  */
138
138
  declare function getCollectionDataAndSendAsObject<TKey extends OnyxKey>(matchingKeys: CollectionKeyBase[], mapping: CallbackToStateMapping<TKey>): void;
139
- /**
140
- * Schedules an update that will be appended to the macro task queue (so it doesn't update the subscribers immediately).
141
- *
142
- * @example
143
- * scheduleSubscriberUpdate(key, value, subscriber => subscriber.initWithStoredValues === false)
144
- */
145
- declare function scheduleSubscriberUpdate<TKey extends OnyxKey>(key: TKey, value: OnyxValue<TKey>, canUpdateSubscriber?: (subscriber?: CallbackToStateMapping<OnyxKey>) => boolean, isProcessingCollectionUpdate?: boolean): Promise<void>;
146
- /**
147
- * This method is similar to scheduleSubscriberUpdate but it is built for working specifically with collections
148
- * so that keysChanged() is triggered for the collection and not keyChanged(). If this was not done, then the
149
- * subscriber callbacks receive the data in a different format than they normally expect and it breaks code.
150
- */
151
- declare function scheduleNotifyCollectionSubscribers<TKey extends OnyxKey>(key: TKey, value: OnyxCollection<KeyValueMapping[TKey]>, previousValue?: OnyxCollection<KeyValueMapping[TKey]>): Promise<void>;
152
139
  /**
153
140
  * Remove a key from Onyx and update the subscribers
154
141
  */
@@ -164,7 +151,7 @@ declare function retryOperation<TMethod extends RetriableOnyxOperation>(error: E
164
151
  /**
165
152
  * Notifies subscribers and writes current value to cache
166
153
  */
167
- declare function broadcastUpdate<TKey extends OnyxKey>(key: TKey, value: OnyxValue<TKey>, hasChanged?: boolean): Promise<void>;
154
+ declare function broadcastUpdate<TKey extends OnyxKey>(key: TKey, value: OnyxValue<TKey>, hasChanged?: boolean): void;
168
155
  declare function hasPendingMergeForKey(key: OnyxKey): boolean;
169
156
  /**
170
157
  * Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
@@ -303,8 +290,6 @@ declare const OnyxUtils: {
303
290
  keyChanged: typeof keyChanged;
304
291
  sendDataToConnection: typeof sendDataToConnection;
305
292
  getCollectionDataAndSendAsObject: typeof getCollectionDataAndSendAsObject;
306
- scheduleSubscriberUpdate: typeof scheduleSubscriberUpdate;
307
- scheduleNotifyCollectionSubscribers: typeof scheduleNotifyCollectionSubscribers;
308
293
  remove: typeof remove;
309
294
  reportStorageQuota: typeof reportStorageQuota;
310
295
  retryOperation: typeof retryOperation;
package/dist/OnyxUtils.js CHANGED
@@ -73,8 +73,6 @@ const MAX_STORAGE_OPERATION_RETRY_ATTEMPTS = 5;
73
73
  // Key/value store of Onyx key and arrays of values to merge
74
74
  let mergeQueue = {};
75
75
  let mergeQueuePromise = {};
76
- // Used to schedule subscriber update to the macro task queue
77
- let nextMacrotaskPromise = null;
78
76
  // Holds a mapping of all the React components that want their state subscribed to a store key
79
77
  let callbackToStateMapping = {};
80
78
  // Holds a mapping of the connected key to the subscriptionID for faster lookups
@@ -476,36 +474,42 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
476
474
  const isSubscribedToCollectionMemberKey = OnyxKeys_1.default.isCollectionMemberKey(collectionKey, subscriber.key);
477
475
  // Regular Onyx.connect() subscriber found.
478
476
  if (typeof subscriber.callback === 'function') {
479
- // If they are subscribed to the collection key and using waitForCollectionCallback then we'll
480
- // send the whole cached collection.
481
- if (isSubscribedToCollectionKey) {
482
- if (subscriber.waitForCollectionCallback) {
483
- subscriber.callback(cachedCollection, subscriber.key, partialCollection);
477
+ try {
478
+ // If they are subscribed to the collection key and using waitForCollectionCallback then we'll
479
+ // send the whole cached collection.
480
+ if (isSubscribedToCollectionKey) {
481
+ lastConnectionCallbackData.set(subscriber.subscriptionID, { value: cachedCollection, matchedKey: subscriber.key });
482
+ if (subscriber.waitForCollectionCallback) {
483
+ subscriber.callback(cachedCollection, subscriber.key, partialCollection);
484
+ continue;
485
+ }
486
+ // If they are not using waitForCollectionCallback then we notify the subscriber with
487
+ // the new merged data but only for any keys in the partial collection.
488
+ const dataKeys = Object.keys(partialCollection !== null && partialCollection !== void 0 ? partialCollection : {});
489
+ for (const dataKey of dataKeys) {
490
+ if ((0, fast_equals_1.deepEqual)(cachedCollection[dataKey], previousCollection[dataKey])) {
491
+ continue;
492
+ }
493
+ subscriber.callback(cachedCollection[dataKey], dataKey);
494
+ }
484
495
  continue;
485
496
  }
486
- // If they are not using waitForCollectionCallback then we notify the subscriber with
487
- // the new merged data but only for any keys in the partial collection.
488
- const dataKeys = Object.keys(partialCollection !== null && partialCollection !== void 0 ? partialCollection : {});
489
- for (const dataKey of dataKeys) {
490
- if ((0, fast_equals_1.deepEqual)(cachedCollection[dataKey], previousCollection[dataKey])) {
497
+ // And if the subscriber is specifically only tracking a particular collection member key then we will
498
+ // notify them with the cached data for that key only.
499
+ if (isSubscribedToCollectionMemberKey) {
500
+ if ((0, fast_equals_1.deepEqual)(cachedCollection[subscriber.key], previousCollection[subscriber.key])) {
491
501
  continue;
492
502
  }
493
- subscriber.callback(cachedCollection[dataKey], dataKey);
494
- }
495
- continue;
496
- }
497
- // And if the subscriber is specifically only tracking a particular collection member key then we will
498
- // notify them with the cached data for that key only.
499
- if (isSubscribedToCollectionMemberKey) {
500
- if ((0, fast_equals_1.deepEqual)(cachedCollection[subscriber.key], previousCollection[subscriber.key])) {
503
+ const subscriberCallback = subscriber.callback;
504
+ subscriberCallback(cachedCollection[subscriber.key], subscriber.key);
505
+ lastConnectionCallbackData.set(subscriber.subscriptionID, { value: cachedCollection[subscriber.key], matchedKey: subscriber.key });
501
506
  continue;
502
507
  }
503
- const subscriberCallback = subscriber.callback;
504
- subscriberCallback(cachedCollection[subscriber.key], subscriber.key);
505
- lastConnectionCallbackData.set(subscriber.subscriptionID, cachedCollection[subscriber.key]);
506
508
  continue;
507
509
  }
508
- continue;
510
+ catch (error) {
511
+ Logger.logAlert(`[OnyxUtils.keysChanged] Subscriber callback threw an error for key '${collectionKey}': ${error}`);
512
+ }
509
513
  }
510
514
  }
511
515
  }
@@ -546,27 +550,35 @@ function keyChanged(key, value, canUpdateSubscriber = () => true, isProcessingCo
546
550
  }
547
551
  // Subscriber is a regular call to connect() and provided a callback
548
552
  if (typeof subscriber.callback === 'function') {
549
- if (lastConnectionCallbackData.has(subscriber.subscriptionID) && lastConnectionCallbackData.get(subscriber.subscriptionID) === value) {
550
- continue;
551
- }
552
- if (OnyxKeys_1.default.isCollectionKey(subscriber.key) && subscriber.waitForCollectionCallback) {
553
- // Skip individual key changes for collection callbacks during collection updates
554
- // to prevent duplicate callbacks - the collection update will handle this properly
555
- if (isProcessingCollectionUpdate) {
553
+ try {
554
+ const lastData = lastConnectionCallbackData.get(subscriber.subscriptionID);
555
+ if (lastData && lastData.matchedKey === key && lastData.value === value) {
556
556
  continue;
557
557
  }
558
- let cachedCollection = cachedCollections[subscriber.key];
559
- if (!cachedCollection) {
560
- cachedCollection = getCachedCollection(subscriber.key);
561
- cachedCollections[subscriber.key] = cachedCollection;
558
+ if (OnyxKeys_1.default.isCollectionKey(subscriber.key) && subscriber.waitForCollectionCallback) {
559
+ // Skip individual key changes for collection callbacks during collection updates
560
+ // to prevent duplicate callbacks - the collection update will handle this properly
561
+ if (isProcessingCollectionUpdate) {
562
+ continue;
563
+ }
564
+ let cachedCollection = cachedCollections[subscriber.key];
565
+ if (!cachedCollection) {
566
+ cachedCollection = getCachedCollection(subscriber.key);
567
+ cachedCollections[subscriber.key] = cachedCollection;
568
+ }
569
+ cachedCollection[key] = value;
570
+ lastConnectionCallbackData.set(subscriber.subscriptionID, { value: cachedCollection, matchedKey: subscriber.key });
571
+ subscriber.callback(cachedCollection, subscriber.key, { [key]: value });
572
+ continue;
562
573
  }
563
- cachedCollection[key] = value;
564
- subscriber.callback(cachedCollection, subscriber.key, { [key]: value });
574
+ const subscriberCallback = subscriber.callback;
575
+ subscriberCallback(value, key);
576
+ lastConnectionCallbackData.set(subscriber.subscriptionID, { value, matchedKey: key });
565
577
  continue;
566
578
  }
567
- const subscriberCallback = subscriber.callback;
568
- subscriberCallback(value, key);
569
- lastConnectionCallbackData.set(subscriber.subscriptionID, value);
579
+ catch (error) {
580
+ Logger.logAlert(`[OnyxUtils.keyChanged] Subscriber callback threw an error for key '${key}': ${error}`);
581
+ }
570
582
  continue;
571
583
  }
572
584
  console.error('Warning: Found a matching subscriber to a key that changed, but no callback could be found.');
@@ -575,22 +587,34 @@ function keyChanged(key, value, canUpdateSubscriber = () => true, isProcessingCo
575
587
  /**
576
588
  * Sends the data obtained from the keys to the connection.
577
589
  */
578
- function sendDataToConnection(mapping, value, matchedKey) {
590
+ function sendDataToConnection(mapping, matchedKey) {
579
591
  var _a, _b;
580
592
  // If the mapping no longer exists then we should not send any data.
581
593
  // This means our subscriber was disconnected.
582
594
  if (!callbackToStateMapping[mapping.subscriptionID]) {
583
595
  return;
584
596
  }
597
+ // Always read the latest value from cache to avoid stale or duplicate data.
598
+ // For collection subscribers with waitForCollectionCallback, read the full collection.
599
+ // For individual key subscribers, read just that key's value.
600
+ let value;
601
+ if (OnyxKeys_1.default.isCollectionKey(mapping.key) && mapping.waitForCollectionCallback) {
602
+ const collection = getCachedCollection(mapping.key);
603
+ value = Object.keys(collection).length > 0 ? collection : undefined;
604
+ }
605
+ else {
606
+ value = OnyxCache_1.default.get(matchedKey !== null && matchedKey !== void 0 ? matchedKey : mapping.key);
607
+ }
585
608
  // For regular callbacks, we never want to pass null values, but always just undefined if a value is not set in cache or storage.
586
- const valueToPass = value === null ? undefined : value;
587
- const lastValue = lastConnectionCallbackData.get(mapping.subscriptionID);
588
- lastConnectionCallbackData.get(mapping.subscriptionID);
589
- // If the value has not changed we do not need to trigger the callback
590
- if (lastConnectionCallbackData.has(mapping.subscriptionID) && valueToPass === lastValue) {
609
+ value = value === null ? undefined : value;
610
+ const lastData = lastConnectionCallbackData.get(mapping.subscriptionID);
611
+ // If the value has not changed for the same key we do not need to trigger the callback.
612
+ // We compare matchedKey to avoid suppressing callbacks for different collection members
613
+ // that happen to have shallow-equal values (e.g. during hydration racing with set()).
614
+ if (lastData && lastData.matchedKey === matchedKey && (0, fast_equals_1.shallowEqual)(lastData.value, value)) {
591
615
  return;
592
616
  }
593
- (_b = (_a = mapping).callback) === null || _b === void 0 ? void 0 : _b.call(_a, valueToPass, matchedKey);
617
+ (_b = (_a = mapping).callback) === null || _b === void 0 ? void 0 : _b.call(_a, value, matchedKey);
594
618
  }
595
619
  /**
596
620
  * We check to see if this key is flagged as safe for eviction and add it to the recentlyAccessedKeys list so that when we
@@ -609,50 +633,16 @@ function addKeyToRecentlyAccessedIfNeeded(key) {
609
633
  * Gets the data for a given an array of matching keys, combines them into an object, and sends the result back to the subscriber.
610
634
  */
611
635
  function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
612
- multiGet(matchingKeys).then((dataMap) => {
613
- const data = Object.fromEntries(dataMap.entries());
614
- sendDataToConnection(mapping, data, mapping.key);
636
+ multiGet(matchingKeys).then(() => {
637
+ sendDataToConnection(mapping, mapping.key);
615
638
  });
616
639
  }
617
- /**
618
- * Delays promise resolution until the next macrotask to prevent race condition if the key subscription is in progress.
619
- *
620
- * @param callback The keyChanged/keysChanged callback
621
- * */
622
- function prepareSubscriberUpdate(callback) {
623
- if (!nextMacrotaskPromise) {
624
- nextMacrotaskPromise = new Promise((resolve) => {
625
- setTimeout(() => {
626
- nextMacrotaskPromise = null;
627
- resolve();
628
- }, 0);
629
- });
630
- }
631
- return Promise.all([nextMacrotaskPromise, Promise.resolve().then(callback)]).then();
632
- }
633
- /**
634
- * Schedules an update that will be appended to the macro task queue (so it doesn't update the subscribers immediately).
635
- *
636
- * @example
637
- * scheduleSubscriberUpdate(key, value, subscriber => subscriber.initWithStoredValues === false)
638
- */
639
- function scheduleSubscriberUpdate(key, value, canUpdateSubscriber = () => true, isProcessingCollectionUpdate = false) {
640
- return prepareSubscriberUpdate(() => keyChanged(key, value, canUpdateSubscriber, isProcessingCollectionUpdate));
641
- }
642
- /**
643
- * This method is similar to scheduleSubscriberUpdate but it is built for working specifically with collections
644
- * so that keysChanged() is triggered for the collection and not keyChanged(). If this was not done, then the
645
- * subscriber callbacks receive the data in a different format than they normally expect and it breaks code.
646
- */
647
- function scheduleNotifyCollectionSubscribers(key, value, previousValue) {
648
- return prepareSubscriberUpdate(() => keysChanged(key, value, previousValue));
649
- }
650
640
  /**
651
641
  * Remove a key from Onyx and update the subscribers
652
642
  */
653
643
  function remove(key, isProcessingCollectionUpdate) {
654
644
  OnyxCache_1.default.drop(key);
655
- scheduleSubscriberUpdate(key, undefined, undefined, isProcessingCollectionUpdate);
645
+ keyChanged(key, undefined, undefined, isProcessingCollectionUpdate);
656
646
  if (OnyxKeys_1.default.isRamOnlyKey(key)) {
657
647
  return Promise.resolve();
658
648
  }
@@ -720,7 +710,7 @@ function broadcastUpdate(key, value, hasChanged) {
720
710
  else {
721
711
  OnyxCache_1.default.addToAccessedKeys(key);
722
712
  }
723
- return scheduleSubscriberUpdate(key, value, (subscriber) => hasChanged || (subscriber === null || subscriber === void 0 ? void 0 : subscriber.initWithStoredValues) === false).then(() => undefined);
713
+ keyChanged(key, value, (subscriber) => hasChanged || (subscriber === null || subscriber === void 0 ? void 0 : subscriber.initWithStoredValues) === false);
724
714
  }
725
715
  function hasPendingMergeForKey(key) {
726
716
  return !!mergeQueue[key];
@@ -943,7 +933,7 @@ function subscribeToKey(connectOptions) {
943
933
  const matchedKey = OnyxKeys_1.default.isCollectionKey(mapping.key) && mapping.waitForCollectionCallback ? mapping.key : undefined;
944
934
  // Here we cannot use batching because the nullish value is expected to be set immediately for default props
945
935
  // or they will be undefined.
946
- sendDataToConnection(mapping, null, matchedKey);
936
+ sendDataToConnection(mapping, matchedKey);
947
937
  return;
948
938
  }
949
939
  // When using a callback subscriber we will either trigger the provided callback for each key we find or combine all values
@@ -956,15 +946,15 @@ function subscribeToKey(connectOptions) {
956
946
  return;
957
947
  }
958
948
  // We did not opt into using waitForCollectionCallback mode so the callback is called for every matching key.
959
- multiGet(matchingKeys).then((values) => {
960
- for (const [key, val] of values.entries()) {
961
- sendDataToConnection(mapping, val, key);
949
+ multiGet(matchingKeys).then(() => {
950
+ for (const key of matchingKeys) {
951
+ sendDataToConnection(mapping, key);
962
952
  }
963
953
  });
964
954
  return;
965
955
  }
966
956
  // If we are not subscribed to a collection key then there's only a single key to send an update for.
967
- get(mapping.key).then((val) => sendDataToConnection(mapping, val, mapping.key));
957
+ get(mapping.key).then(() => sendDataToConnection(mapping, mapping.key));
968
958
  return;
969
959
  }
970
960
  console.error('Warning: Onyx.connect() was found without a callback');
@@ -1098,21 +1088,20 @@ function setWithRetry({ key, value, options }, retryAttempt) {
1098
1088
  const hasChanged = (options === null || options === void 0 ? void 0 : options.skipCacheCheck) ? true : OnyxCache_1.default.hasValueChanged(key, valueWithoutNestedNullValues);
1099
1089
  OnyxUtils.logKeyChanged(OnyxUtils.METHOD.SET, key, value, hasChanged);
1100
1090
  // This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
1101
- const updatePromise = OnyxUtils.broadcastUpdate(key, valueWithoutNestedNullValues, hasChanged);
1091
+ OnyxUtils.broadcastUpdate(key, valueWithoutNestedNullValues, hasChanged);
1102
1092
  // If the value has not changed and this isn't a retry attempt, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
1103
1093
  if (!hasChanged && !retryAttempt) {
1104
- return updatePromise;
1094
+ return Promise.resolve();
1105
1095
  }
1106
1096
  // If a key is a RAM-only key or a member of RAM-only collection, we skip the step that modifies the storage
1107
1097
  if (OnyxKeys_1.default.isRamOnlyKey(key)) {
1108
1098
  OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.SET, key, valueWithoutNestedNullValues);
1109
- return updatePromise;
1099
+ return Promise.resolve();
1110
1100
  }
1111
1101
  return storage_1.default.setItem(key, valueWithoutNestedNullValues)
1112
1102
  .catch((error) => OnyxUtils.retryOperation(error, setWithRetry, { key, value: valueWithoutNestedNullValues, options }, retryAttempt))
1113
1103
  .then(() => {
1114
1104
  OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.SET, key, valueWithoutNestedNullValues);
1115
- return updatePromise;
1116
1105
  });
1117
1106
  }
1118
1107
  /**
@@ -1142,16 +1131,16 @@ function multiSetWithRetry(data, retryAttempt) {
1142
1131
  }, {});
1143
1132
  }
1144
1133
  const keyValuePairsToSet = OnyxUtils.prepareKeyValuePairsForStorage(newData, true);
1145
- const updatePromises = keyValuePairsToSet.map(([key, value]) => {
1134
+ for (const [key, value] of keyValuePairsToSet) {
1146
1135
  // When we use multiSet to set a key we want to clear the current delta changes from Onyx.merge that were queued
1147
1136
  // before the value was set. If Onyx.merge is currently reading the old value from storage, it will then not apply the changes.
1148
1137
  if (OnyxUtils.hasPendingMergeForKey(key)) {
1149
1138
  delete OnyxUtils.getMergeQueue()[key];
1150
1139
  }
1151
- // Update cache and optimistically inform subscribers on the next tick
1140
+ // Update cache and optimistically inform subscribers
1152
1141
  OnyxCache_1.default.set(key, value);
1153
- return OnyxUtils.scheduleSubscriberUpdate(key, value);
1154
- });
1142
+ keyChanged(key, value);
1143
+ }
1155
1144
  const keyValuePairsToStore = keyValuePairsToSet.filter((keyValuePair) => {
1156
1145
  const [key] = keyValuePair;
1157
1146
  // Filter out the RAM-only key value pairs, as they should not be saved to storage
@@ -1161,9 +1150,7 @@ function multiSetWithRetry(data, retryAttempt) {
1161
1150
  .catch((error) => OnyxUtils.retryOperation(error, multiSetWithRetry, newData, retryAttempt))
1162
1151
  .then(() => {
1163
1152
  OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.MULTI_SET, undefined, newData);
1164
- return Promise.all(updatePromises);
1165
- })
1166
- .then(() => undefined);
1153
+ });
1167
1154
  }
1168
1155
  /**
1169
1156
  * Sets a collection by replacing all existing collection members with new values.
@@ -1216,17 +1203,16 @@ function setCollectionWithRetry({ collectionKey, collection }, retryAttempt) {
1216
1203
  const previousCollection = OnyxUtils.getCachedCollection(collectionKey);
1217
1204
  for (const [key, value] of keyValuePairs)
1218
1205
  OnyxCache_1.default.set(key, value);
1219
- const updatePromise = OnyxUtils.scheduleNotifyCollectionSubscribers(collectionKey, mutableCollection, previousCollection);
1206
+ keysChanged(collectionKey, mutableCollection, previousCollection);
1220
1207
  // RAM-only keys are not supposed to be saved to storage
1221
1208
  if (OnyxKeys_1.default.isRamOnlyKey(collectionKey)) {
1222
1209
  OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.SET_COLLECTION, undefined, mutableCollection);
1223
- return updatePromise;
1210
+ return;
1224
1211
  }
1225
1212
  return storage_1.default.multiSet(keyValuePairs)
1226
1213
  .catch((error) => OnyxUtils.retryOperation(error, setCollectionWithRetry, { collectionKey, collection }, retryAttempt))
1227
1214
  .then(() => {
1228
1215
  OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.SET_COLLECTION, undefined, mutableCollection);
1229
- return updatePromise;
1230
1216
  });
1231
1217
  });
1232
1218
  }
@@ -1333,7 +1319,7 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
1333
1319
  // and update all subscribers
1334
1320
  const promiseUpdate = previousCollectionPromise.then((previousCollection) => {
1335
1321
  OnyxCache_1.default.merge(finalMergedCollection);
1336
- return scheduleNotifyCollectionSubscribers(collectionKey, finalMergedCollection, previousCollection);
1322
+ keysChanged(collectionKey, finalMergedCollection, previousCollection);
1337
1323
  });
1338
1324
  return Promise.all(promises)
1339
1325
  .catch((error) => retryOperation(error, mergeCollectionWithPatches, { collectionKey, collection: resultCollection, mergeReplaceNullPatches, isProcessingCollectionUpdate }, retryAttempt))
@@ -1386,16 +1372,15 @@ function partialSetCollection({ collectionKey, collection }, retryAttempt) {
1386
1372
  const keyValuePairs = prepareKeyValuePairsForStorage(mutableCollection, true, undefined, true);
1387
1373
  for (const [key, value] of keyValuePairs)
1388
1374
  OnyxCache_1.default.set(key, value);
1389
- const updatePromise = scheduleNotifyCollectionSubscribers(collectionKey, mutableCollection, previousCollection);
1375
+ keysChanged(collectionKey, mutableCollection, previousCollection);
1390
1376
  if (OnyxKeys_1.default.isRamOnlyKey(collectionKey)) {
1391
1377
  sendActionToDevTools(METHOD.SET_COLLECTION, undefined, mutableCollection);
1392
- return updatePromise;
1378
+ return;
1393
1379
  }
1394
1380
  return storage_1.default.multiSet(keyValuePairs)
1395
1381
  .catch((error) => retryOperation(error, partialSetCollection, { collectionKey, collection }, retryAttempt))
1396
1382
  .then(() => {
1397
1383
  sendActionToDevTools(METHOD.SET_COLLECTION, undefined, mutableCollection);
1398
- return updatePromise;
1399
1384
  });
1400
1385
  });
1401
1386
  }
@@ -1432,8 +1417,6 @@ const OnyxUtils = {
1432
1417
  keyChanged,
1433
1418
  sendDataToConnection,
1434
1419
  getCollectionDataAndSendAsObject,
1435
- scheduleSubscriberUpdate,
1436
- scheduleNotifyCollectionSubscribers,
1437
1420
  remove,
1438
1421
  reportStorageQuota,
1439
1422
  retryOperation,
@@ -6,12 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_native_nitro_sqlite_1 = require("react-native-nitro-sqlite");
7
7
  const react_native_device_info_1 = require("react-native-device-info");
8
8
  const utils_1 = __importDefault(require("../../utils"));
9
- // By default, NitroSQLite does not accept nullish values due to current limitations in Nitro Modules.
10
- // This flag enables a feature in NitroSQLite that allows for nullish values to be passed to operations, such as "execute" or "executeBatch".
11
- // Simple null handling can potentially add a minor performance overhead,
12
- // since parameters and results from SQLite queries need to be parsed from and to JavaScript nullish values.
13
- // https://github.com/margelo/react-native-nitro-sqlite#sending-and-receiving-nullish-values
14
- (0, react_native_nitro_sqlite_1.enableSimpleNullHandling)();
15
9
  const DB_NAME = 'OnyxDB';
16
10
  /**
17
11
  * Prevents the stringifying of the object markers.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.60",
3
+ "version": "3.0.62",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",
@@ -91,8 +91,8 @@
91
91
  "react": "18.2.0",
92
92
  "react-native": "0.76.3",
93
93
  "react-native-device-info": "^10.3.0",
94
- "react-native-nitro-modules": "^0.27.2",
95
- "react-native-nitro-sqlite": "^9.2.0",
94
+ "react-native-nitro-modules": "^0.35.0",
95
+ "react-native-nitro-sqlite": "^9.6.0",
96
96
  "react-test-renderer": "18.2.0",
97
97
  "reassure": "1.4.0",
98
98
  "ts-node": "^10.9.2",
@@ -104,8 +104,8 @@
104
104
  "react": ">=18.1.0",
105
105
  "react-native": ">=0.75.0",
106
106
  "react-native-device-info": "^10.3.0",
107
- "react-native-nitro-modules": ">=0.27.2",
108
- "react-native-nitro-sqlite": "^9.2.0"
107
+ "react-native-nitro-modules": ">=0.35.0",
108
+ "react-native-nitro-sqlite": "^9.6.0"
109
109
  },
110
110
  "peerDependenciesMeta": {
111
111
  "idb-keyval": {