react-native-onyx 3.0.31 → 3.0.33

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
@@ -2,7 +2,7 @@ import * as Logger from './Logger';
2
2
  import type { CollectionKeyBase, ConnectOptions, InitOptions, OnyxKey, OnyxMergeCollectionInput, OnyxSetCollectionInput, OnyxMergeInput, OnyxMultiSetInput, OnyxSetInput, OnyxUpdate, SetOptions } from './types';
3
3
  import type { Connection } from './OnyxConnectionManager';
4
4
  /** Initialize the store with actions and listening for storage events */
5
- declare function init({ keys, initialKeyStates, evictableKeys, maxCachedKeysCount, shouldSyncMultipleInstances, enablePerformanceMetrics, enableDevTools, skippableCollectionMemberIDs, }: InitOptions): void;
5
+ declare function init({ keys, initialKeyStates, evictableKeys, maxCachedKeysCount, shouldSyncMultipleInstances, enablePerformanceMetrics, enableDevTools, skippableCollectionMemberIDs, snapshotMergeKeys, }: InitOptions): void;
6
6
  /**
7
7
  * Connects to an Onyx key given the options passed and listens to its changes.
8
8
  * This method will be deprecated soon. Please use `Onyx.connectWithoutView()` instead.
package/dist/Onyx.js CHANGED
@@ -48,7 +48,7 @@ const GlobalSettings = __importStar(require("./GlobalSettings"));
48
48
  const metrics_1 = __importDefault(require("./metrics"));
49
49
  const OnyxMerge_1 = __importDefault(require("./OnyxMerge"));
50
50
  /** Initialize the store with actions and listening for storage events */
51
- function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], maxCachedKeysCount = 1000, shouldSyncMultipleInstances = !!global.localStorage, enablePerformanceMetrics = false, enableDevTools = true, skippableCollectionMemberIDs = [], }) {
51
+ function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], maxCachedKeysCount = 1000, shouldSyncMultipleInstances = !!global.localStorage, enablePerformanceMetrics = false, enableDevTools = true, skippableCollectionMemberIDs = [], snapshotMergeKeys = [], }) {
52
52
  var _a;
53
53
  if (enablePerformanceMetrics) {
54
54
  GlobalSettings.setPerformanceMetricsEnabled(true);
@@ -57,6 +57,7 @@ function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], maxCachedK
57
57
  (0, DevTools_1.initDevTools)(enableDevTools);
58
58
  storage_1.default.init();
59
59
  OnyxUtils_1.default.setSkippableCollectionMemberIDs(new Set(skippableCollectionMemberIDs));
60
+ OnyxUtils_1.default.setSnapshotMergeKeys(new Set(snapshotMergeKeys));
60
61
  if (shouldSyncMultipleInstances) {
61
62
  (_a = storage_1.default.keepInstancesSync) === null || _a === void 0 ? void 0 : _a.call(storage_1.default, (key, value) => {
62
63
  OnyxCache_1.default.set(key, value);
@@ -289,8 +290,10 @@ function clear(keysToPreserve = []) {
289
290
  var _a;
290
291
  OnyxCache_1.default.clearNullishStorageKeys();
291
292
  const keysToBeClearedFromStorage = [];
292
- const keyValuesToResetAsCollection = {};
293
293
  const keyValuesToResetIndividually = {};
294
+ // We need to store old and new values for collection keys to properly notify subscribers when clearing Onyx
295
+ // because the notification process needs the old values in cache but at that point they will be already removed from it.
296
+ const keyValuesToResetAsCollection = {};
294
297
  const allKeys = new Set([...cachedKeys, ...initialKeys]);
295
298
  // The only keys that should not be cleared are:
296
299
  // 1. Anything specifically passed in keysToPreserve (because some keys like language preferences, offline
@@ -320,9 +323,10 @@ function clear(keysToPreserve = []) {
320
323
  }
321
324
  if (collectionKey) {
322
325
  if (!keyValuesToResetAsCollection[collectionKey]) {
323
- keyValuesToResetAsCollection[collectionKey] = {};
326
+ keyValuesToResetAsCollection[collectionKey] = { oldValues: {}, newValues: {} };
324
327
  }
325
- keyValuesToResetAsCollection[collectionKey][key] = newValue !== null && newValue !== void 0 ? newValue : undefined;
328
+ keyValuesToResetAsCollection[collectionKey].oldValues[key] = oldValue;
329
+ keyValuesToResetAsCollection[collectionKey].newValues[key] = newValue !== null && newValue !== void 0 ? newValue : undefined;
326
330
  }
327
331
  else {
328
332
  keyValuesToResetIndividually[key] = newValue !== null && newValue !== void 0 ? newValue : undefined;
@@ -341,7 +345,7 @@ function clear(keysToPreserve = []) {
341
345
  updatePromises.push(OnyxUtils_1.default.scheduleSubscriberUpdate(key, value));
342
346
  }
343
347
  for (const [key, value] of Object.entries(keyValuesToResetAsCollection)) {
344
- updatePromises.push(OnyxUtils_1.default.scheduleNotifyCollectionSubscribers(key, value));
348
+ updatePromises.push(OnyxUtils_1.default.scheduleNotifyCollectionSubscribers(key, value.newValues, value.oldValues));
345
349
  }
346
350
  const defaultKeyValuePairs = Object.entries(Object.keys(defaultKeyStates)
347
351
  .filter((key) => !keysToPreserve.includes(key))
@@ -34,10 +34,18 @@ declare function getDeferredInitTask(): DeferredTask;
34
34
  * Getter - returns the skippable collection member IDs.
35
35
  */
36
36
  declare function getSkippableCollectionMemberIDs(): Set<string>;
37
+ /**
38
+ * Getter - returns the snapshot merge keys allowlist.
39
+ */
40
+ declare function getSnapshotMergeKeys(): Set<string>;
37
41
  /**
38
42
  * Setter - sets the skippable collection member IDs.
39
43
  */
40
44
  declare function setSkippableCollectionMemberIDs(ids: Set<string>): void;
45
+ /**
46
+ * Setter - sets the snapshot merge keys allowlist.
47
+ */
48
+ declare function setSnapshotMergeKeys(keys: Set<string>): void;
41
49
  /**
42
50
  * Sets the initial values for the Onyx store
43
51
  *
@@ -354,6 +362,8 @@ declare const OnyxUtils: {
354
362
  unsubscribeFromKey: typeof unsubscribeFromKey;
355
363
  getSkippableCollectionMemberIDs: typeof getSkippableCollectionMemberIDs;
356
364
  setSkippableCollectionMemberIDs: typeof setSkippableCollectionMemberIDs;
365
+ getSnapshotMergeKeys: typeof getSnapshotMergeKeys;
366
+ setSnapshotMergeKeys: typeof setSnapshotMergeKeys;
357
367
  storeKeyBySubscriptions: typeof storeKeyBySubscriptions;
358
368
  deleteKeyBySubscriptions: typeof deleteKeyBySubscriptions;
359
369
  addKeyToRecentlyAccessedIfNeeded: typeof addKeyToRecentlyAccessedIfNeeded;
package/dist/OnyxUtils.js CHANGED
@@ -38,7 +38,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.clearOnyxUtilsInternals = clearOnyxUtilsInternals;
40
40
  const fast_equals_1 = require("fast-equals");
41
- const pick_1 = __importDefault(require("lodash/pick"));
42
41
  const underscore_1 = __importDefault(require("underscore"));
43
42
  const DevTools_1 = __importDefault(require("./DevTools"));
44
43
  const Logger = __importStar(require("./Logger"));
@@ -94,6 +93,8 @@ let lastSubscriptionID = 0;
94
93
  const deferredInitTask = (0, createDeferredTask_1.default)();
95
94
  // Holds a set of collection member IDs which updates will be ignored when using Onyx methods.
96
95
  let skippableCollectionMemberIDs = new Set();
96
+ // Holds a set of keys that should always be merged into snapshot entries.
97
+ let snapshotMergeKeys = new Set();
97
98
  function getSnapshotKey() {
98
99
  return snapshotKey;
99
100
  }
@@ -127,12 +128,24 @@ function getDeferredInitTask() {
127
128
  function getSkippableCollectionMemberIDs() {
128
129
  return skippableCollectionMemberIDs;
129
130
  }
131
+ /**
132
+ * Getter - returns the snapshot merge keys allowlist.
133
+ */
134
+ function getSnapshotMergeKeys() {
135
+ return snapshotMergeKeys;
136
+ }
130
137
  /**
131
138
  * Setter - sets the skippable collection member IDs.
132
139
  */
133
140
  function setSkippableCollectionMemberIDs(ids) {
134
141
  skippableCollectionMemberIDs = ids;
135
142
  }
143
+ /**
144
+ * Setter - sets the snapshot merge keys allowlist.
145
+ */
146
+ function setSnapshotMergeKeys(keys) {
147
+ snapshotMergeKeys = keys;
148
+ }
136
149
  /**
137
150
  * Sets the initial values for the Onyx store
138
151
  *
@@ -1011,7 +1024,14 @@ function updateSnapshots(data, mergeFn) {
1011
1024
  continue;
1012
1025
  }
1013
1026
  const oldValue = updatedData[key] || {};
1014
- const newValue = (0, pick_1.default)(value, Object.keys(snapshotData[key]));
1027
+ // Snapshot entries are stored as a "shape" of the last known data per key, so by default we only
1028
+ // merge fields that already exist in the snapshot to avoid unintentionally bloating snapshot data.
1029
+ // Some clients need specific fields (like pending status) even when they are missing in the snapshot,
1030
+ // so we allow an explicit, opt-in list of keys to always include during snapshot merges.
1031
+ const snapshotExistingKeys = Object.keys(snapshotData[key] || {});
1032
+ const allowedNewKeys = getSnapshotMergeKeys();
1033
+ const keysToCopy = new Set([...snapshotExistingKeys, ...allowedNewKeys]);
1034
+ const newValue = typeof value === 'object' && value !== null ? utils_1.default.pick(value, [...keysToCopy]) : {};
1015
1035
  updatedData = Object.assign(Object.assign({}, updatedData), { [key]: Object.assign(oldValue, newValue) });
1016
1036
  }
1017
1037
  // Skip the update if there's no data to be merged
@@ -1411,6 +1431,8 @@ const OnyxUtils = {
1411
1431
  unsubscribeFromKey,
1412
1432
  getSkippableCollectionMemberIDs,
1413
1433
  setSkippableCollectionMemberIDs,
1434
+ getSnapshotMergeKeys,
1435
+ setSnapshotMergeKeys,
1414
1436
  storeKeyBySubscriptions,
1415
1437
  deleteKeyBySubscriptions,
1416
1438
  addKeyToRecentlyAccessedIfNeeded,
package/dist/types.d.ts CHANGED
@@ -364,6 +364,14 @@ type InitOptions = {
364
364
  * Additionally, any subscribers from these keys to won't receive any data from Onyx.
365
365
  */
366
366
  skippableCollectionMemberIDs?: string[];
367
+ /**
368
+ * A list of field names that should always be merged into snapshot entries even if those fields are
369
+ * missing in the snapshot. Snapshots are saved "views" of a key's data used to populate read-only
370
+ * or cached lists, and by default Onyx only merges fields that already exist in that saved view.
371
+ * Use this to opt-in to additional fields that must appear in snapshots (for example, pending flags)
372
+ * without hardcoding app-specific logic inside Onyx.
373
+ */
374
+ snapshotMergeKeys?: string[];
367
375
  };
368
376
  type GenericFunction = (...args: any[]) => any;
369
377
  /**
package/dist/useOnyx.js CHANGED
@@ -254,7 +254,7 @@ function useOnyx(key, options, dependencies = []) {
254
254
  OnyxSnapshotCache_1.default.setCachedResult(key, cacheKey, resultRef.current);
255
255
  }
256
256
  return resultRef.current;
257
- }, [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]);
257
+ }, [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, previousKey]);
258
258
  const subscribe = (0, react_1.useCallback)((onStoreChange) => {
259
259
  isConnectingRef.current = true;
260
260
  onStoreChangeFnRef.current = onStoreChange;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.31",
3
+ "version": "3.0.33",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",