react-native-onyx 3.0.76 → 3.0.78

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 CHANGED
@@ -1336,33 +1336,39 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
1336
1336
  // We can safely remove nested null values when using (multi-)set,
1337
1337
  // because we will simply overwrite the existing values in storage.
1338
1338
  const keyValuePairsForNewCollection = prepareKeyValuePairsForStorage(newCollection, true);
1339
- const promises = [];
1340
- // We need to get the previously existing values so we can compare the new ones
1341
- // against them, to avoid unnecessary subscriber updates.
1342
- const previousCollectionPromise = Promise.all(existingKeys.map((key) => get(key).then((value) => [key, value]))).then(Object.fromEntries);
1343
- // New keys will be added via multiSet while existing keys will be updated using multiMerge
1344
- // This is because setting a key that doesn't exist yet with multiMerge will throw errors
1345
- // We can skip this step for RAM-only keys as they should never be saved to storage
1346
- if (!OnyxKeys_1.default.isRamOnlyKey(collectionKey) && keyValuePairsForExistingCollection.length > 0) {
1347
- promises.push(storage_1.default.multiMerge(keyValuePairsForExistingCollection));
1348
- }
1349
- // We can skip this step for RAM-only keys as they should never be saved to storage
1350
- if (!OnyxKeys_1.default.isRamOnlyKey(collectionKey) && keyValuePairsForNewCollection.length > 0) {
1351
- promises.push(storage_1.default.multiSet(keyValuePairsForNewCollection));
1352
- }
1353
1339
  // finalMergedCollection contains all the keys that were merged, without the keys of incompatible updates
1354
1340
  const finalMergedCollection = Object.assign(Object.assign({}, existingKeyCollection), newCollection);
1355
- // Prefill cache if necessary by calling get() on any existing keys and then merge original data to cache
1356
- // and update all subscribers
1357
- const promiseUpdate = previousCollectionPromise.then((previousCollection) => {
1341
+ // Pre-warm cache for any existing storage keys that aren't yet in cache. get() is a no-op
1342
+ // (sync-resolved) for cache hits, and on a cache miss it reads from storage and writes the
1343
+ // value back to cache. This is required so the subsequent cache.merge() merges the new delta
1344
+ // into the real previous storage value (rather than starting from `undefined` and dropping
1345
+ // the existing keys).
1346
+ return Promise.all(existingKeys.map((key) => get(key))).then(() => {
1347
+ // Snapshot previous values from the (now-warm) cache for keysChanged's diff, then update
1348
+ // cache and notify subscribers synchronously BEFORE issuing storage writes. This matches
1349
+ // the cache-first / storage-second invariant followed by every other Onyx write method
1350
+ // (setWithRetry, applyMerge, setCollectionWithRetry, partialSetCollection, clear),
1351
+ // ensuring subscribers still reflect the merged data even if the subsequent storage
1352
+ // write fails.
1353
+ const previousCollection = getCachedCollection(collectionKey, existingKeys);
1358
1354
  OnyxCache_1.default.merge(finalMergedCollection);
1359
1355
  keysChanged(collectionKey, finalMergedCollection, previousCollection);
1360
- });
1361
- return Promise.all(promises)
1362
- .catch((error) => retryOperation(error, mergeCollectionWithPatches, { collectionKey, collection: resultCollection, mergeReplaceNullPatches, isProcessingCollectionUpdate }, retryAttempt))
1363
- .then(() => {
1364
- sendActionToDevTools(METHOD.MERGE_COLLECTION, undefined, resultCollection);
1365
- return promiseUpdate;
1356
+ const promises = [];
1357
+ // New keys will be added via multiSet while existing keys will be updated using multiMerge
1358
+ // This is because setting a key that doesn't exist yet with multiMerge will throw errors
1359
+ // We can skip this step for RAM-only keys as they should never be saved to storage
1360
+ if (!OnyxKeys_1.default.isRamOnlyKey(collectionKey) && keyValuePairsForExistingCollection.length > 0) {
1361
+ promises.push(storage_1.default.multiMerge(keyValuePairsForExistingCollection));
1362
+ }
1363
+ // We can skip this step for RAM-only keys as they should never be saved to storage
1364
+ if (!OnyxKeys_1.default.isRamOnlyKey(collectionKey) && keyValuePairsForNewCollection.length > 0) {
1365
+ promises.push(storage_1.default.multiSet(keyValuePairsForNewCollection));
1366
+ }
1367
+ return Promise.all(promises)
1368
+ .catch((error) => retryOperation(error, mergeCollectionWithPatches, { collectionKey, collection: resultCollection, mergeReplaceNullPatches, isProcessingCollectionUpdate }, retryAttempt))
1369
+ .then(() => {
1370
+ sendActionToDevTools(METHOD.MERGE_COLLECTION, undefined, resultCollection);
1371
+ });
1366
1372
  });
1367
1373
  })
1368
1374
  .then(() => undefined);
@@ -58,27 +58,15 @@ function degradePerformance(error) {
58
58
  * Runs a piece of code and degrades performance if certain errors are thrown
59
59
  */
60
60
  function tryOrDegradePerformance(fn, waitForInitialization = true) {
61
- return new Promise((resolve, reject) => {
62
- const promise = waitForInitialization ? initPromise : Promise.resolve();
63
- promise.then(() => {
64
- try {
65
- resolve(fn());
66
- }
67
- catch (error) {
68
- // Test for known critical errors that the storage provider throws, e.g. when storage is full
69
- if (error instanceof Error) {
70
- // IndexedDB error when storage is full (https://github.com/Expensify/App/issues/29403)
71
- if (error.message.includes('Internal error opening backing store for indexedDB.open')) {
72
- degradePerformance(error);
73
- }
74
- // catch the error if DB connection can not be established/DB can not be created
75
- if (error.message.includes('IDBKeyVal store could not be created')) {
76
- degradePerformance(error);
77
- }
78
- }
79
- reject(error);
80
- }
81
- });
61
+ const initialization = waitForInitialization ? initPromise : Promise.resolve();
62
+ return initialization
63
+ .then(() => fn())
64
+ .catch((error) => {
65
+ // catch the error if DB connection can not be established/DB can not be created
66
+ if (error instanceof Error && error.message.includes('IDBKeyVal store could not be created')) {
67
+ degradePerformance(error);
68
+ }
69
+ return Promise.reject(error);
82
70
  });
83
71
  }
84
72
  const storage = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.76",
3
+ "version": "3.0.78",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",