react-native-onyx 3.0.33 → 3.0.34

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/README.md CHANGED
@@ -460,6 +460,25 @@ Onyx.init({
460
460
  });
461
461
  ```
462
462
 
463
+ ### Using RAM-only keys
464
+
465
+ You can choose not to save certain keys on disk and keep them RAM-only, that way their values will reset with each session. You just have to pass an array of `ramOnlyKeys` to the `Onyx.init` method. You can mark entire collections as RAM-only by including the collection key (e.g., `ONYXKEYS.COLLECTION.TEMP_DATA`). This will make all members of that collection RAM-only. Individual collection member keys cannot be selectively marked as RAM-only.
466
+
467
+ ```javascript
468
+ import Onyx from 'react-native-onyx';
469
+
470
+ Onyx.init({
471
+ keys: ONYXKEYS,
472
+ ramOnlyKeys: [
473
+ ONYXKEYS.RAM_ONLY_KEY_1,
474
+ ONYXKEYS.RAM_ONLY_KEY_2,
475
+ ONYXKEYS.COLLECTION.TEMP_DATA,
476
+ ],
477
+ });
478
+ ```
479
+
480
+ > Note: RAM-only keys still consume memory and will remain in cache until explicitly cleared or until Onyx.clear() is called. Use them judiciously for truly ephemeral data.
481
+
463
482
  ### Usage
464
483
 
465
484
  The extension interface is pretty simple, on the left sidebar you can see all the updates made to the local storage, in ascending order, and on the right pane you can see the whole the current state, payload of an action and the diff between the previous state and the current state after the action was triggered.
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, snapshotMergeKeys, }: InitOptions): void;
5
+ declare function init({ keys, initialKeyStates, evictableKeys, maxCachedKeysCount, shouldSyncMultipleInstances, enablePerformanceMetrics, enableDevTools, skippableCollectionMemberIDs, ramOnlyKeys, 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 = [], snapshotMergeKeys = [], }) {
51
+ function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], maxCachedKeysCount = 1000, shouldSyncMultipleInstances = !!global.localStorage, enablePerformanceMetrics = false, enableDevTools = true, skippableCollectionMemberIDs = [], ramOnlyKeys = [], snapshotMergeKeys = [], }) {
52
52
  var _a;
53
53
  if (enablePerformanceMetrics) {
54
54
  GlobalSettings.setPerformanceMetricsEnabled(true);
@@ -58,6 +58,7 @@ function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], maxCachedK
58
58
  storage_1.default.init();
59
59
  OnyxUtils_1.default.setSkippableCollectionMemberIDs(new Set(skippableCollectionMemberIDs));
60
60
  OnyxUtils_1.default.setSnapshotMergeKeys(new Set(snapshotMergeKeys));
61
+ OnyxCache_1.default.setRamOnlyKeys(new Set(ramOnlyKeys));
61
62
  if (shouldSyncMultipleInstances) {
62
63
  (_a = storage_1.default.keepInstancesSync) === null || _a === void 0 ? void 0 : _a.call(storage_1.default, (key, value) => {
63
64
  OnyxCache_1.default.set(key, value);
@@ -347,8 +348,9 @@ function clear(keysToPreserve = []) {
347
348
  for (const [key, value] of Object.entries(keyValuesToResetAsCollection)) {
348
349
  updatePromises.push(OnyxUtils_1.default.scheduleNotifyCollectionSubscribers(key, value.newValues, value.oldValues));
349
350
  }
351
+ // Exclude RAM-only keys to prevent them from being saved to storage
350
352
  const defaultKeyValuePairs = Object.entries(Object.keys(defaultKeyStates)
351
- .filter((key) => !keysToPreserve.includes(key))
353
+ .filter((key) => !keysToPreserve.includes(key) && !OnyxUtils_1.default.isRamOnlyKey(key))
352
354
  .reduce((obj, key) => {
353
355
  // eslint-disable-next-line no-param-reassign
354
356
  obj[key] = defaultKeyStates[key];
@@ -36,6 +36,8 @@ declare class OnyxCache {
36
36
  private recentlyAccessedKeys;
37
37
  /** Set of collection keys for fast lookup */
38
38
  private collectionKeys;
39
+ /** Set of RAM-only keys for fast lookup */
40
+ private ramOnlyKeys;
39
41
  constructor();
40
42
  /** Get all the storage keys */
41
43
  getAllKeys(): Set<OnyxKey>;
@@ -165,6 +167,14 @@ declare class OnyxCache {
165
167
  * Get all data for a collection key
166
168
  */
167
169
  getCollectionData(collectionKey: OnyxKey): Record<OnyxKey, OnyxValue<OnyxKey>> | undefined;
170
+ /**
171
+ * Set the RAM-only keys for optimized storage
172
+ */
173
+ setRamOnlyKeys(ramOnlyKeys: Set<OnyxKey>): void;
174
+ /**
175
+ * Check if a key is a RAM-only key
176
+ */
177
+ isRamOnlyKey(key: OnyxKey): boolean;
168
178
  }
169
179
  declare const instance: OnyxCache;
170
180
  export default instance;
package/dist/OnyxCache.js CHANGED
@@ -64,6 +64,8 @@ class OnyxCache {
64
64
  this.recentlyAccessedKeys = new Set();
65
65
  /** Set of collection keys for fast lookup */
66
66
  this.collectionKeys = new Set();
67
+ /** Set of RAM-only keys for fast lookup */
68
+ this.ramOnlyKeys = new Set();
67
69
  this.storageKeys = new Set();
68
70
  this.nullishStorageKeys = new Set();
69
71
  this.recentKeys = new Set();
@@ -71,7 +73,7 @@ class OnyxCache {
71
73
  this.collectionData = {};
72
74
  this.pendingPromises = new Map();
73
75
  // bind all public methods to prevent problems with `this`
74
- (0, bindAll_1.default)(this, 'getAllKeys', 'get', 'hasCacheForKey', 'addKey', 'addNullishStorageKey', 'hasNullishStorageKey', 'clearNullishStorageKeys', 'set', 'drop', 'merge', 'hasPendingTask', 'getTaskPromise', 'captureTask', 'addToAccessedKeys', 'removeLeastRecentlyUsedKeys', 'setRecentKeysLimit', 'setAllKeys', 'setEvictionAllowList', 'getEvictionBlocklist', 'isEvictableKey', 'removeLastAccessedKey', 'addLastAccessedKey', 'addEvictableKeysToRecentlyAccessedList', 'getKeyForEviction', 'setCollectionKeys', 'isCollectionKey', 'getCollectionKey', 'getCollectionData');
76
+ (0, bindAll_1.default)(this, 'getAllKeys', 'get', 'hasCacheForKey', 'addKey', 'addNullishStorageKey', 'hasNullishStorageKey', 'clearNullishStorageKeys', 'set', 'drop', 'merge', 'hasPendingTask', 'getTaskPromise', 'captureTask', 'addToAccessedKeys', 'removeLeastRecentlyUsedKeys', 'setRecentKeysLimit', 'setAllKeys', 'setEvictionAllowList', 'getEvictionBlocklist', 'isEvictableKey', 'removeLastAccessedKey', 'addLastAccessedKey', 'addEvictableKeysToRecentlyAccessedList', 'getKeyForEviction', 'setCollectionKeys', 'isCollectionKey', 'getCollectionKey', 'getCollectionData', 'setRamOnlyKeys', 'isRamOnlyKey');
75
77
  }
76
78
  /** Get all the storage keys */
77
79
  getAllKeys() {
@@ -394,6 +396,18 @@ class OnyxCache {
394
396
  // Return a shallow copy to ensure React detects changes when items are added/removed
395
397
  return Object.assign({}, cachedCollection);
396
398
  }
399
+ /**
400
+ * Set the RAM-only keys for optimized storage
401
+ */
402
+ setRamOnlyKeys(ramOnlyKeys) {
403
+ this.ramOnlyKeys = ramOnlyKeys;
404
+ }
405
+ /**
406
+ * Check if a key is a RAM-only key
407
+ */
408
+ isRamOnlyKey(key) {
409
+ return this.ramOnlyKeys.has(key);
410
+ }
397
411
  }
398
412
  const instance = new OnyxCache();
399
413
  exports.default = instance;
@@ -14,8 +14,10 @@ const applyMerge = (key, existingValue, validChanges) => {
14
14
  OnyxUtils_1.default.logKeyChanged(OnyxUtils_1.default.METHOD.MERGE, key, mergedValue, hasChanged);
15
15
  // This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
16
16
  const updatePromise = OnyxUtils_1.default.broadcastUpdate(key, mergedValue, hasChanged);
17
+ const shouldSkipStorageOperations = !hasChanged || OnyxUtils_1.default.isRamOnlyKey(key);
17
18
  // If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
18
- if (!hasChanged) {
19
+ // If the key is marked as RAM-only, it should not be saved nor updated in the storage.
20
+ if (shouldSkipStorageOperations) {
19
21
  return Promise.resolve({ mergedValue, updatePromise });
20
22
  }
21
23
  // For web platforms we use `setItem` since the object was already merged with its changes before.
@@ -20,8 +20,10 @@ const applyMerge = (key, existingValue, validChanges) => {
20
20
  OnyxUtils_1.default.logKeyChanged(OnyxUtils_1.default.METHOD.MERGE, key, mergedValue, hasChanged);
21
21
  // This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
22
22
  const updatePromise = OnyxUtils_1.default.broadcastUpdate(key, mergedValue, hasChanged);
23
+ const shouldSkipStorageOperations = !hasChanged || OnyxUtils_1.default.isRamOnlyKey(key);
23
24
  // If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
24
- if (!hasChanged) {
25
+ // If the key is marked as RAM-only, it should not be saved nor updated in the storage.
26
+ if (shouldSkipStorageOperations) {
25
27
  return Promise.resolve({ mergedValue, updatePromise });
26
28
  }
27
29
  // For native platforms we use `mergeItem` that will take advantage of JSON_PATCH and JSON_REPLACE SQL operations to
@@ -113,6 +113,24 @@ declare function isCollectionMemberKey<TCollectionKey extends CollectionKeyBase>
113
113
  * @returns true if the key is a collection member, false otherwise
114
114
  */
115
115
  declare function isCollectionMember(key: OnyxKey): boolean;
116
+ /**
117
+ * Checks if a given key is a RAM-only key, RAM-only collection key, or a RAM-only collection member
118
+ *
119
+ * For example:
120
+ *
121
+ * For the following Onyx setup
122
+ *
123
+ * ramOnlyKeys: ["ramOnlyKey", "ramOnlyCollection_"]
124
+ *
125
+ * - `isRamOnlyKey("ramOnlyKey")` would return true
126
+ * - `isRamOnlyKey("ramOnlyCollection_")` would return true
127
+ * - `isRamOnlyKey("ramOnlyCollection_1")` would return true
128
+ * - `isRamOnlyKey("someOtherKey")` would return false
129
+ *
130
+ * @param key - The key to check
131
+ * @returns true if key is a RAM-only key, RAM-only collection key, or a RAM-only collection member
132
+ */
133
+ declare function isRamOnlyKey(key: OnyxKey): boolean;
116
134
  /**
117
135
  * Splits a collection member key into the collection key part and the ID part.
118
136
  * @param key - The collection member key to split.
@@ -376,6 +394,7 @@ declare const OnyxUtils: {
376
394
  setWithRetry: typeof setWithRetry;
377
395
  multiSetWithRetry: typeof multiSetWithRetry;
378
396
  setCollectionWithRetry: typeof setCollectionWithRetry;
397
+ isRamOnlyKey: typeof isRamOnlyKey;
379
398
  };
380
399
  export type { OnyxMethod };
381
400
  export default OnyxUtils;
package/dist/OnyxUtils.js CHANGED
@@ -385,6 +385,34 @@ function isCollectionMember(key) {
385
385
  return false;
386
386
  }
387
387
  }
388
+ /**
389
+ * Checks if a given key is a RAM-only key, RAM-only collection key, or a RAM-only collection member
390
+ *
391
+ * For example:
392
+ *
393
+ * For the following Onyx setup
394
+ *
395
+ * ramOnlyKeys: ["ramOnlyKey", "ramOnlyCollection_"]
396
+ *
397
+ * - `isRamOnlyKey("ramOnlyKey")` would return true
398
+ * - `isRamOnlyKey("ramOnlyCollection_")` would return true
399
+ * - `isRamOnlyKey("ramOnlyCollection_1")` would return true
400
+ * - `isRamOnlyKey("someOtherKey")` would return false
401
+ *
402
+ * @param key - The key to check
403
+ * @returns true if key is a RAM-only key, RAM-only collection key, or a RAM-only collection member
404
+ */
405
+ function isRamOnlyKey(key) {
406
+ try {
407
+ const collectionKey = getCollectionKey(key);
408
+ // If collectionKey exists for a given key, check if it's a RAM-only key
409
+ return OnyxCache_1.default.isRamOnlyKey(collectionKey);
410
+ }
411
+ catch (_a) {
412
+ // If getCollectionKey throws, the key is not a collection member
413
+ }
414
+ return OnyxCache_1.default.isRamOnlyKey(key);
415
+ }
388
416
  /**
389
417
  * Splits a collection member key into the collection key part and the ID part.
390
418
  * @param key - The collection member key to split.
@@ -713,6 +741,9 @@ function scheduleNotifyCollectionSubscribers(key, value, previousValue) {
713
741
  function remove(key, isProcessingCollectionUpdate) {
714
742
  OnyxCache_1.default.drop(key);
715
743
  scheduleSubscriberUpdate(key, undefined, undefined, isProcessingCollectionUpdate);
744
+ if (isRamOnlyKey(key)) {
745
+ return Promise.resolve();
746
+ }
716
747
  return storage_1.default.removeItem(key).then(() => undefined);
717
748
  }
718
749
  function reportStorageQuota() {
@@ -1103,6 +1134,11 @@ function setWithRetry({ key, value, options }, retryAttempt) {
1103
1134
  if (!hasChanged && !retryAttempt) {
1104
1135
  return updatePromise;
1105
1136
  }
1137
+ // If a key is a RAM-only key or a member of RAM-only collection, we skip the step that modifies the storage
1138
+ if (isRamOnlyKey(key)) {
1139
+ OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.SET, key, valueWithoutNestedNullValues);
1140
+ return updatePromise;
1141
+ }
1106
1142
  return storage_1.default.setItem(key, valueWithoutNestedNullValues)
1107
1143
  .catch((error) => OnyxUtils.retryOperation(error, setWithRetry, { key, value: valueWithoutNestedNullValues, options }, retryAttempt))
1108
1144
  .then(() => {
@@ -1147,7 +1183,12 @@ function multiSetWithRetry(data, retryAttempt) {
1147
1183
  OnyxCache_1.default.set(key, value);
1148
1184
  return OnyxUtils.scheduleSubscriberUpdate(key, value);
1149
1185
  });
1150
- return storage_1.default.multiSet(keyValuePairsToSet)
1186
+ const keyValuePairsToStore = keyValuePairsToSet.filter((keyValuePair) => {
1187
+ const [key] = keyValuePair;
1188
+ // Filter out the RAM-only key value pairs, as they should not be saved to storage
1189
+ return !isRamOnlyKey(key);
1190
+ });
1191
+ return storage_1.default.multiSet(keyValuePairsToStore)
1151
1192
  .catch((error) => OnyxUtils.retryOperation(error, multiSetWithRetry, newData, retryAttempt))
1152
1193
  .then(() => {
1153
1194
  OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.MULTI_SET, undefined, newData);
@@ -1207,6 +1248,11 @@ function setCollectionWithRetry({ collectionKey, collection }, retryAttempt) {
1207
1248
  for (const [key, value] of keyValuePairs)
1208
1249
  OnyxCache_1.default.set(key, value);
1209
1250
  const updatePromise = OnyxUtils.scheduleNotifyCollectionSubscribers(collectionKey, mutableCollection, previousCollection);
1251
+ // RAM-only keys are not supposed to be saved to storage
1252
+ if (isRamOnlyKey(collectionKey)) {
1253
+ OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.SET_COLLECTION, undefined, mutableCollection);
1254
+ return updatePromise;
1255
+ }
1210
1256
  return storage_1.default.multiSet(keyValuePairs)
1211
1257
  .catch((error) => OnyxUtils.retryOperation(error, setCollectionWithRetry, { collectionKey, collection }, retryAttempt))
1212
1258
  .then(() => {
@@ -1298,10 +1344,12 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
1298
1344
  const previousCollectionPromise = Promise.all(existingKeys.map((key) => get(key).then((value) => [key, value]))).then(Object.fromEntries);
1299
1345
  // New keys will be added via multiSet while existing keys will be updated using multiMerge
1300
1346
  // This is because setting a key that doesn't exist yet with multiMerge will throw errors
1301
- if (keyValuePairsForExistingCollection.length > 0) {
1347
+ // We can skip this step for RAM-only keys as they should never be saved to storage
1348
+ if (!isRamOnlyKey(collectionKey) && keyValuePairsForExistingCollection.length > 0) {
1302
1349
  promises.push(storage_1.default.multiMerge(keyValuePairsForExistingCollection));
1303
1350
  }
1304
- if (keyValuePairsForNewCollection.length > 0) {
1351
+ // We can skip this step for RAM-only keys as they should never be saved to storage
1352
+ if (!isRamOnlyKey(collectionKey) && keyValuePairsForNewCollection.length > 0) {
1305
1353
  promises.push(storage_1.default.multiSet(keyValuePairsForNewCollection));
1306
1354
  }
1307
1355
  // finalMergedCollection contains all the keys that were merged, without the keys of incompatible updates
@@ -1364,6 +1412,10 @@ function partialSetCollection({ collectionKey, collection }, retryAttempt) {
1364
1412
  for (const [key, value] of keyValuePairs)
1365
1413
  OnyxCache_1.default.set(key, value);
1366
1414
  const updatePromise = scheduleNotifyCollectionSubscribers(collectionKey, mutableCollection, previousCollection);
1415
+ if (isRamOnlyKey(collectionKey)) {
1416
+ sendActionToDevTools(METHOD.SET_COLLECTION, undefined, mutableCollection);
1417
+ return updatePromise;
1418
+ }
1367
1419
  return storage_1.default.multiSet(keyValuePairs)
1368
1420
  .catch((error) => retryOperation(error, partialSetCollection, { collectionKey, collection }, retryAttempt))
1369
1421
  .then(() => {
@@ -1445,6 +1497,7 @@ const OnyxUtils = {
1445
1497
  setWithRetry,
1446
1498
  multiSetWithRetry,
1447
1499
  setCollectionWithRetry,
1500
+ isRamOnlyKey,
1448
1501
  };
1449
1502
  GlobalSettings.addGlobalSettingsChangeListener(({ enablePerformanceMetrics }) => {
1450
1503
  if (!enablePerformanceMetrics) {
package/dist/types.d.ts CHANGED
@@ -364,6 +364,10 @@ 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
+ * Array of keys that when provided to Onyx are flagged as RAM-only keys, and thus are not saved to disk.
369
+ */
370
+ ramOnlyKeys?: OnyxKey[];
367
371
  /**
368
372
  * A list of field names that should always be merged into snapshot entries even if those fields are
369
373
  * missing in the snapshot. Snapshots are saved "views" of a key's data used to populate read-only
package/dist/useOnyx.d.ts CHANGED
@@ -8,6 +8,7 @@ type UseOnyxOptions<TKey extends OnyxKey, TReturnValue> = {
8
8
  canEvict?: boolean;
9
9
  /**
10
10
  * If set to `false`, then no data will be prefilled into the component.
11
+ * @deprecated This param is going to be removed soon. Use RAM-only keys instead.
11
12
  */
12
13
  initWithStoredValues?: boolean;
13
14
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.33",
3
+ "version": "3.0.34",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",