react-native-onyx 3.0.62 → 3.0.63

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
@@ -314,42 +314,24 @@ If a platform needs to use a separate library (like using MMVK for react-native)
314
314
 
315
315
  [Docs](./API.md)
316
316
 
317
- # Cache Eviction
317
+ # Storage Eviction
318
318
 
319
319
  Different platforms come with varying storage capacities and Onyx has a way to gracefully fail when those storage limits are encountered. When Onyx fails to set or modify a key the following steps are taken:
320
- 1. Onyx looks at a list of recently accessed keys (access is defined as subscribed to or modified) and locates the key that was least recently accessed
321
- 2. It then deletes this key and retries the original operation
320
+ 1. Onyx looks at a list of evictable keys ordered by recent access and locates the least recently accessed one
321
+ 2. It then deletes this key from both cache and storage, and retries the original operation
322
+ 3. This process repeats up to 5 times until the write succeeds or no more evictable keys are available
322
323
 
323
324
  By default, Onyx will not evict anything from storage and will presume all keys are "unsafe" to remove unless explicitly told otherwise.
324
325
 
325
- **To flag a key as safe for removal:**
326
- - Add the key to the `evictableKeys` option in `Onyx.init(options)`
327
- - Implement `canEvict` in the Onyx config for each component subscribing to a key
328
- - The key will only be deleted when all subscribers return `true` for `canEvict`
326
+ **To flag a key as safe for removal**, add the key to the `evictableKeys` option in `Onyx.init(options)`:
329
327
 
330
- e.g.
331
328
  ```js
332
329
  Onyx.init({
333
330
  evictableKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
334
331
  });
335
332
  ```
336
333
 
337
- ```js
338
- const ReportActionsView = ({reportID, isActiveReport}) => {
339
- const [reportActions] = useOnyx(
340
- `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}_`,
341
- {canEvict: () => !isActiveReport}
342
- );
343
-
344
- return (
345
- <View>
346
- {/* Render with reportActions data */}
347
- </View>
348
- );
349
- };
350
-
351
- export default ReportActionsView;
352
- ```
334
+ Only individual (non-collection) keys matching the `evictableKeys` patterns will be considered for eviction. Collection keys themselves cannot be evicted — only their individual members can.
353
335
 
354
336
  # Debug mode
355
337
 
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, enableDevTools, skippableCollectionMemberIDs, ramOnlyKeys, snapshotMergeKeys, }: InitOptions): void;
5
+ declare function init({ keys, initialKeyStates, evictableKeys, shouldSyncMultipleInstances, 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
@@ -47,7 +47,7 @@ const logMessages_1 = __importDefault(require("./logMessages"));
47
47
  const OnyxConnectionManager_1 = __importDefault(require("./OnyxConnectionManager"));
48
48
  const OnyxMerge_1 = __importDefault(require("./OnyxMerge"));
49
49
  /** Initialize the store with actions and listening for storage events */
50
- function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], maxCachedKeysCount = 1000, shouldSyncMultipleInstances = !!global.localStorage, enableDevTools = true, skippableCollectionMemberIDs = [], ramOnlyKeys = [], snapshotMergeKeys = [], }) {
50
+ function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], shouldSyncMultipleInstances = !!global.localStorage, enableDevTools = true, skippableCollectionMemberIDs = [], ramOnlyKeys = [], snapshotMergeKeys = [], }) {
51
51
  var _a;
52
52
  (0, DevTools_1.initDevTools)(enableDevTools);
53
53
  storage_1.default.init();
@@ -69,9 +69,6 @@ function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], maxCachedK
69
69
  OnyxUtils_1.default.keyChanged(key, value, undefined, isKeyCollectionMember);
70
70
  });
71
71
  }
72
- if (maxCachedKeysCount > 0) {
73
- OnyxCache_1.default.setRecentKeysLimit(maxCachedKeysCount);
74
- }
75
72
  OnyxUtils_1.default.initStoreValues(keys, initialKeyStates, evictableKeys);
76
73
  // Initialize all of our keys with data provided then give green light to any pending connections.
77
74
  // addEvictableKeysToRecentlyAccessedList must run after initializeWithDefaultKeyStates because
@@ -15,8 +15,6 @@ declare class OnyxCache {
15
15
  private storageKeys;
16
16
  /** A list of keys where a nullish value has been fetched from storage before, but the key still exists in cache */
17
17
  private nullishStorageKeys;
18
- /** Unique list of keys maintained in access order (most recent at the end) */
19
- private recentKeys;
20
18
  /** A map of cached values */
21
19
  private storageMap;
22
20
  /** Cache of complete collection data objects for O(1) retrieval */
@@ -26,12 +24,8 @@ declare class OnyxCache {
26
24
  * Using a map yields better performance on operations such a delete
27
25
  */
28
26
  private pendingPromises;
29
- /** Maximum size of the keys store din cache */
30
- private maxRecentKeysSize;
31
27
  /** List of keys that are safe to remove when we reach max storage */
32
28
  private evictionAllowList;
33
- /** Map of keys and connection arrays whose keys will never be automatically evicted */
34
- private evictionBlocklist;
35
29
  /** List of keys that have been directly subscribed to or recently modified from least to most recent */
36
30
  private recentlyAccessedKeys;
37
31
  constructor();
@@ -61,11 +55,8 @@ declare class OnyxCache {
61
55
  clearNullishStorageKeys(): void;
62
56
  /** Check whether cache has data for the given key */
63
57
  hasCacheForKey(key: OnyxKey): boolean;
64
- /**
65
- * Get a cached value from storage
66
- * @param [shouldReindexCache] – This is an LRU cache, and by default accessing a value will make it become last in line to be evicted. This flag can be used to skip that and just access the value directly without side-effects.
67
- */
68
- get(key: OnyxKey, shouldReindexCache?: boolean): OnyxValue<OnyxKey>;
58
+ /** Get a cached value from storage */
59
+ get(key: OnyxKey): OnyxValue<OnyxKey>;
69
60
  /**
70
61
  * Set's a key value in cache
71
62
  * Adds the key to the storage keys list as well
@@ -96,12 +87,6 @@ declare class OnyxCache {
96
87
  * @param taskName - unique name for the task
97
88
  */
98
89
  captureTask(taskName: CacheTask, promise: Promise<OnyxValue<OnyxKey>>): Promise<OnyxValue<OnyxKey>>;
99
- /** Adds a key to the top of the recently accessed keys */
100
- addToAccessedKeys(key: OnyxKey): void;
101
- /** Remove keys that don't fall into the range of recently used keys */
102
- removeLeastRecentlyUsedKeys(): void;
103
- /** Set the recent keys list size */
104
- setRecentKeysLimit(limit: number): void;
105
90
  /** Check if the value has changed */
106
91
  hasValueChanged(key: OnyxKey, value: OnyxValue<OnyxKey>): boolean;
107
92
  /**
@@ -109,10 +94,6 @@ declare class OnyxCache {
109
94
  * @param keys - Array of OnyxKeys that are safe to evict
110
95
  */
111
96
  setEvictionAllowList(keys: OnyxKey[]): void;
112
- /**
113
- * Get the eviction block list that prevents keys from being evicted
114
- */
115
- getEvictionBlocklist(): Record<OnyxKey, string[] | undefined>;
116
97
  /**
117
98
  * Checks to see if this key has been flagged as safe for removal.
118
99
  * @param testKey - Key to check
@@ -138,7 +119,7 @@ declare class OnyxCache {
138
119
  */
139
120
  addEvictableKeysToRecentlyAccessedList(isCollectionKeyFn: (key: OnyxKey) => boolean, getAllKeysFn: () => Promise<Set<OnyxKey>>): Promise<void>;
140
121
  /**
141
- * Finds a key that can be safely evicted
122
+ * Finds the least recently accessed key that can be safely evicted from storage.
142
123
  */
143
124
  getKeyForEviction(): OnyxKey | undefined;
144
125
  /**
package/dist/OnyxCache.js CHANGED
@@ -21,22 +21,17 @@ exports.TASK = TASK;
21
21
  */
22
22
  class OnyxCache {
23
23
  constructor() {
24
- /** Maximum size of the keys store din cache */
25
- this.maxRecentKeysSize = 0;
26
24
  /** List of keys that are safe to remove when we reach max storage */
27
25
  this.evictionAllowList = [];
28
- /** Map of keys and connection arrays whose keys will never be automatically evicted */
29
- this.evictionBlocklist = {};
30
26
  /** List of keys that have been directly subscribed to or recently modified from least to most recent */
31
27
  this.recentlyAccessedKeys = new Set();
32
28
  this.storageKeys = new Set();
33
29
  this.nullishStorageKeys = new Set();
34
- this.recentKeys = new Set();
35
30
  this.storageMap = {};
36
31
  this.collectionData = {};
37
32
  this.pendingPromises = new Map();
38
33
  // bind all public methods to prevent problems with `this`
39
- (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', 'getCollectionData', 'hasValueChanged');
34
+ (0, bindAll_1.default)(this, 'getAllKeys', 'get', 'hasCacheForKey', 'addKey', 'addNullishStorageKey', 'hasNullishStorageKey', 'clearNullishStorageKeys', 'set', 'drop', 'merge', 'hasPendingTask', 'getTaskPromise', 'captureTask', 'setAllKeys', 'setEvictionAllowList', 'isEvictableKey', 'removeLastAccessedKey', 'addLastAccessedKey', 'addEvictableKeysToRecentlyAccessedList', 'getKeyForEviction', 'setCollectionKeys', 'getCollectionData', 'hasValueChanged');
40
35
  }
41
36
  /** Get all the storage keys */
42
37
  getAllKeys() {
@@ -82,14 +77,8 @@ class OnyxCache {
82
77
  hasCacheForKey(key) {
83
78
  return this.storageMap[key] !== undefined || this.hasNullishStorageKey(key);
84
79
  }
85
- /**
86
- * Get a cached value from storage
87
- * @param [shouldReindexCache] – This is an LRU cache, and by default accessing a value will make it become last in line to be evicted. This flag can be used to skip that and just access the value directly without side-effects.
88
- */
89
- get(key, shouldReindexCache = true) {
90
- if (shouldReindexCache) {
91
- this.addToAccessedKeys(key);
92
- }
80
+ /** Get a cached value from storage */
81
+ get(key) {
93
82
  return this.storageMap[key];
94
83
  }
95
84
  /**
@@ -98,7 +87,6 @@ class OnyxCache {
98
87
  */
99
88
  set(key, value) {
100
89
  this.addKey(key);
101
- this.addToAccessedKeys(key);
102
90
  // When a key is explicitly set in cache, we can remove it from the list of nullish keys,
103
91
  // since it will either be set to a non nullish value or removed from the cache completely.
104
92
  this.nullishStorageKeys.delete(key);
@@ -134,7 +122,6 @@ class OnyxCache {
134
122
  delete this.collectionData[key];
135
123
  }
136
124
  this.storageKeys.delete(key);
137
- this.recentKeys.delete(key);
138
125
  OnyxKeys_1.default.deregisterMemberKey(key);
139
126
  }
140
127
  /**
@@ -151,7 +138,6 @@ class OnyxCache {
151
138
  }).result);
152
139
  for (const [key, value] of Object.entries(data)) {
153
140
  this.addKey(key);
154
- this.addToAccessedKeys(key);
155
141
  const collectionKey = OnyxKeys_1.default.getCollectionKey(key);
156
142
  if (value === null || value === undefined) {
157
143
  this.addNullishStorageKey(key);
@@ -200,48 +186,9 @@ class OnyxCache {
200
186
  this.pendingPromises.set(taskName, returnPromise);
201
187
  return returnPromise;
202
188
  }
203
- /** Adds a key to the top of the recently accessed keys */
204
- addToAccessedKeys(key) {
205
- this.recentKeys.delete(key);
206
- this.recentKeys.add(key);
207
- }
208
- /** Remove keys that don't fall into the range of recently used keys */
209
- removeLeastRecentlyUsedKeys() {
210
- const numKeysToRemove = this.recentKeys.size - this.maxRecentKeysSize;
211
- if (numKeysToRemove <= 0) {
212
- return;
213
- }
214
- const iterator = this.recentKeys.values();
215
- const keysToRemove = [];
216
- const recentKeysArray = Array.from(this.recentKeys);
217
- const mostRecentKey = recentKeysArray[recentKeysArray.length - 1];
218
- let iterResult = iterator.next();
219
- while (!iterResult.done) {
220
- const key = iterResult.value;
221
- // Don't consider the most recently accessed key for eviction
222
- // This ensures we don't immediately evict a key we just added
223
- if (key !== undefined && key !== mostRecentKey && this.isEvictableKey(key)) {
224
- keysToRemove.push(key);
225
- }
226
- iterResult = iterator.next();
227
- }
228
- for (const key of keysToRemove) {
229
- delete this.storageMap[key];
230
- // Remove from collection data cache if this is a collection member
231
- const collectionKey = OnyxKeys_1.default.getCollectionKey(key);
232
- if (collectionKey && this.collectionData[collectionKey]) {
233
- delete this.collectionData[collectionKey][key];
234
- }
235
- this.recentKeys.delete(key);
236
- }
237
- }
238
- /** Set the recent keys list size */
239
- setRecentKeysLimit(limit) {
240
- this.maxRecentKeysSize = limit;
241
- }
242
189
  /** Check if the value has changed */
243
190
  hasValueChanged(key, value) {
244
- const currentValue = this.get(key, false);
191
+ const currentValue = this.get(key);
245
192
  return !(0, fast_equals_1.deepEqual)(currentValue, value);
246
193
  }
247
194
  /**
@@ -251,12 +198,6 @@ class OnyxCache {
251
198
  setEvictionAllowList(keys) {
252
199
  this.evictionAllowList = keys;
253
200
  }
254
- /**
255
- * Get the eviction block list that prevents keys from being evicted
256
- */
257
- getEvictionBlocklist() {
258
- return this.evictionBlocklist;
259
- }
260
201
  /**
261
202
  * Checks to see if this key has been flagged as safe for removal.
262
203
  * @param testKey - Key to check
@@ -304,15 +245,12 @@ class OnyxCache {
304
245
  });
305
246
  }
306
247
  /**
307
- * Finds a key that can be safely evicted
248
+ * Finds the least recently accessed key that can be safely evicted from storage.
308
249
  */
309
250
  getKeyForEviction() {
310
- for (const key of this.recentlyAccessedKeys) {
311
- if (!this.evictionBlocklist[key]) {
312
- return key;
313
- }
314
- }
315
- return undefined;
251
+ // recentlyAccessedKeys is ordered from least to most recently accessed,
252
+ // so the first element is the best candidate for eviction.
253
+ return this.recentlyAccessedKeys.values().next().value;
316
254
  }
317
255
  /**
318
256
  * Set the collection keys for optimized storage
@@ -72,15 +72,6 @@ declare class OnyxConnectionManager {
72
72
  * Refreshes the connection manager's session ID.
73
73
  */
74
74
  refreshSessionID(): void;
75
- /**
76
- * Adds the connection to the eviction block list. Connections added to this list can never be evicted.
77
- * */
78
- addToEvictionBlockList(connection: Connection): void;
79
- /**
80
- * Removes a connection previously added to this list
81
- * which will enable it to be evicted again.
82
- */
83
- removeFromEvictionBlockList(connection: Connection): void;
84
75
  }
85
76
  declare const connectionManager: OnyxConnectionManager;
86
77
  export default connectionManager;
@@ -41,7 +41,6 @@ const Logger = __importStar(require("./Logger"));
41
41
  const OnyxUtils_1 = __importDefault(require("./OnyxUtils"));
42
42
  const OnyxKeys_1 = __importDefault(require("./OnyxKeys"));
43
43
  const Str = __importStar(require("./Str"));
44
- const OnyxCache_1 = __importDefault(require("./OnyxCache"));
45
44
  const OnyxSnapshotCache_1 = __importDefault(require("./OnyxSnapshotCache"));
46
45
  /**
47
46
  * Manages Onyx connections of `Onyx.connect()` and `useOnyx()` subscribers.
@@ -52,7 +51,7 @@ class OnyxConnectionManager {
52
51
  this.lastCallbackID = 0;
53
52
  this.sessionID = Str.guid();
54
53
  // Binds all public methods to prevent problems with `this`.
55
- (0, bindAll_1.default)(this, 'generateConnectionID', 'fireCallbacks', 'connect', 'disconnect', 'disconnectAll', 'refreshSessionID', 'addToEvictionBlockList', 'removeFromEvictionBlockList');
54
+ (0, bindAll_1.default)(this, 'generateConnectionID', 'fireCallbacks', 'connect', 'disconnect', 'disconnectAll', 'refreshSessionID');
56
55
  }
57
56
  /**
58
57
  * Generates a connection ID based on the `connectOptions` object passed to the function.
@@ -164,7 +163,6 @@ class OnyxConnectionManager {
164
163
  // If the connection's callbacks map is empty we can safely unsubscribe from the Onyx key.
165
164
  if (connectionMetadata.callbacks.size === 0) {
166
165
  OnyxUtils_1.default.unsubscribeFromKey(connectionMetadata.subscriptionID);
167
- this.removeFromEvictionBlockList(connection);
168
166
  this.connectionsMap.delete(connection.id);
169
167
  }
170
168
  }
@@ -172,11 +170,8 @@ class OnyxConnectionManager {
172
170
  * Disconnect all subscribers from Onyx.
173
171
  */
174
172
  disconnectAll() {
175
- for (const [connectionID, connectionMetadata] of this.connectionsMap.entries()) {
173
+ for (const connectionMetadata of this.connectionsMap.values()) {
176
174
  OnyxUtils_1.default.unsubscribeFromKey(connectionMetadata.subscriptionID);
177
- for (const callbackID of connectionMetadata.callbacks.keys()) {
178
- this.removeFromEvictionBlockList({ id: connectionID, callbackID });
179
- }
180
175
  }
181
176
  this.connectionsMap.clear();
182
177
  // Clear snapshot cache when all connections are disconnected
@@ -190,49 +185,6 @@ class OnyxConnectionManager {
190
185
  // Clear snapshot cache when session refreshes to avoid stale cache issues
191
186
  OnyxSnapshotCache_1.default.clear();
192
187
  }
193
- /**
194
- * Adds the connection to the eviction block list. Connections added to this list can never be evicted.
195
- * */
196
- addToEvictionBlockList(connection) {
197
- var _a;
198
- if (!connection) {
199
- Logger.logInfo(`[ConnectionManager] Attempted to add connection to eviction block list passing an undefined connection object.`);
200
- return;
201
- }
202
- const connectionMetadata = this.connectionsMap.get(connection.id);
203
- if (!connectionMetadata) {
204
- Logger.logInfo(`[ConnectionManager] Attempted to add connection to eviction block list but no connection was found.`);
205
- return;
206
- }
207
- const evictionBlocklist = OnyxCache_1.default.getEvictionBlocklist();
208
- if (!evictionBlocklist[connectionMetadata.onyxKey]) {
209
- evictionBlocklist[connectionMetadata.onyxKey] = [];
210
- }
211
- (_a = evictionBlocklist[connectionMetadata.onyxKey]) === null || _a === void 0 ? void 0 : _a.push(`${connection.id}_${connection.callbackID}`);
212
- }
213
- /**
214
- * Removes a connection previously added to this list
215
- * which will enable it to be evicted again.
216
- */
217
- removeFromEvictionBlockList(connection) {
218
- var _a, _b, _c;
219
- if (!connection) {
220
- Logger.logInfo(`[ConnectionManager] Attempted to remove connection from eviction block list passing an undefined connection object.`);
221
- return;
222
- }
223
- const connectionMetadata = this.connectionsMap.get(connection.id);
224
- if (!connectionMetadata) {
225
- Logger.logInfo(`[ConnectionManager] Attempted to remove connection from eviction block list but no connection was found.`);
226
- return;
227
- }
228
- const evictionBlocklist = OnyxCache_1.default.getEvictionBlocklist();
229
- evictionBlocklist[connectionMetadata.onyxKey] =
230
- (_b = (_a = evictionBlocklist[connectionMetadata.onyxKey]) === null || _a === void 0 ? void 0 : _a.filter((evictionKey) => evictionKey !== `${connection.id}_${connection.callbackID}`)) !== null && _b !== void 0 ? _b : [];
231
- // Remove the key if there are no more subscribers.
232
- if (((_c = evictionBlocklist[connectionMetadata.onyxKey]) === null || _c === void 0 ? void 0 : _c.length) === 0) {
233
- delete evictionBlocklist[connectionMetadata.onyxKey];
234
- }
235
- }
236
188
  }
237
189
  const connectionManager = new OnyxConnectionManager();
238
190
  exports.default = connectionManager;
@@ -40,7 +40,7 @@ declare class OnyxSnapshotCache {
40
40
  * - `selector`: Different selectors produce different results, so each selector needs its own cache entry
41
41
  * - `initWithStoredValues`: This flag changes the initial loading behavior and affects the returned fetch status
42
42
  *
43
- * Other options like `canEvict` and `reuseConnection` don't affect the data transformation
43
+ * Other options like `reuseConnection` don't affect the data transformation
44
44
  * or timing behavior of getSnapshot, so they're excluded from the cache key for better cache hit rates.
45
45
  */
46
46
  registerConsumer<TKey extends OnyxKey, TReturnValue>(options: Pick<UseOnyxOptions<TKey, TReturnValue>, 'selector' | 'initWithStoredValues'>): string;
@@ -37,7 +37,7 @@ class OnyxSnapshotCache {
37
37
  * - `selector`: Different selectors produce different results, so each selector needs its own cache entry
38
38
  * - `initWithStoredValues`: This flag changes the initial loading behavior and affects the returned fetch status
39
39
  *
40
- * Other options like `canEvict` and `reuseConnection` don't affect the data transformation
40
+ * Other options like `reuseConnection` don't affect the data transformation
41
41
  * or timing behavior of getSnapshot, so they're excluded from the cache key for better cache hit rates.
42
42
  */
43
43
  registerConsumer(options) {
@@ -127,11 +127,6 @@ declare function keyChanged<TKey extends OnyxKey>(key: TKey, value: OnyxValue<TK
127
127
  * Sends the data obtained from the keys to the connection.
128
128
  */
129
129
  declare function sendDataToConnection<TKey extends OnyxKey>(mapping: CallbackToStateMapping<TKey>, matchedKey: TKey | undefined): void;
130
- /**
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
- * run out of storage the least recently accessed key can be removed.
133
- */
134
- declare function addKeyToRecentlyAccessedIfNeeded<TKey extends OnyxKey>(key: TKey): void;
135
130
  /**
136
131
  * 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
132
  */
@@ -312,7 +307,6 @@ declare const OnyxUtils: {
312
307
  setSnapshotMergeKeys: typeof setSnapshotMergeKeys;
313
308
  storeKeyBySubscriptions: typeof storeKeyBySubscriptions;
314
309
  deleteKeyBySubscriptions: typeof deleteKeyBySubscriptions;
315
- addKeyToRecentlyAccessedIfNeeded: typeof addKeyToRecentlyAccessedIfNeeded;
316
310
  reduceCollectionWithSelector: typeof reduceCollectionWithSelector;
317
311
  updateSnapshots: typeof updateSnapshots;
318
312
  mergeCollectionWithPatches: typeof mergeCollectionWithPatches;
package/dist/OnyxUtils.js CHANGED
@@ -521,8 +521,8 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
521
521
  */
522
522
  function keyChanged(key, value, canUpdateSubscriber = () => true, isProcessingCollectionUpdate = false) {
523
523
  var _a, _b;
524
- // Add or remove this key from the recentlyAccessedKeys lists
525
- if (value !== null) {
524
+ // Add or remove this key from the recentlyAccessedKeys list
525
+ if (value !== null && value !== undefined) {
526
526
  OnyxCache_1.default.addLastAccessedKey(key, OnyxKeys_1.default.isCollectionKey(key));
527
527
  }
528
528
  else {
@@ -616,19 +616,6 @@ function sendDataToConnection(mapping, matchedKey) {
616
616
  }
617
617
  (_b = (_a = mapping).callback) === null || _b === void 0 ? void 0 : _b.call(_a, value, matchedKey);
618
618
  }
619
- /**
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
621
- * run out of storage the least recently accessed key can be removed.
622
- */
623
- function addKeyToRecentlyAccessedIfNeeded(key) {
624
- if (!OnyxCache_1.default.isEvictableKey(key)) {
625
- return;
626
- }
627
- // Add the key to recentKeys first (this makes it the most recent key)
628
- OnyxCache_1.default.addToAccessedKeys(key);
629
- // Try to free some cache whenever we connect to a safe eviction key
630
- OnyxCache_1.default.removeLeastRecentlyUsedKeys();
631
- }
632
619
  /**
633
620
  * Gets the data for a given an array of matching keys, combines them into an object, and sends the result back to the subscriber.
634
621
  */
@@ -683,7 +670,7 @@ function retryOperation(error, onyxMethod, defaultParams, retryAttempt) {
683
670
  // @ts-expect-error No overload matches this call.
684
671
  return onyxMethod(defaultParams, nextRetryAttempt);
685
672
  }
686
- // Find the first key that we can remove that has no subscribers in our blocklist
673
+ // Find the least recently accessed evictable key that we can remove
687
674
  const keyForRemoval = OnyxCache_1.default.getKeyForEviction();
688
675
  if (!keyForRemoval) {
689
676
  // If we have no acceptable keys to remove then we are possibly trying to save mission critical data. If this is the case,
@@ -692,7 +679,7 @@ function retryOperation(error, onyxMethod, defaultParams, retryAttempt) {
692
679
  Logger.logAlert('Out of storage. But found no acceptable keys to remove.');
693
680
  return reportStorageQuota();
694
681
  }
695
- // Remove the least recently viewed key that is not currently being accessed and retry.
682
+ // Remove the least recently accessed key and retry.
696
683
  Logger.logInfo(`Out of storage. Evicting least recently accessed key (${keyForRemoval}) and retrying.`);
697
684
  reportStorageQuota();
698
685
  // @ts-expect-error No overload matches this call.
@@ -707,9 +694,6 @@ function broadcastUpdate(key, value, hasChanged) {
707
694
  if (hasChanged) {
708
695
  OnyxCache_1.default.set(key, value);
709
696
  }
710
- else {
711
- OnyxCache_1.default.addToAccessedKeys(key);
712
- }
713
697
  keyChanged(key, value, (subscriber) => hasChanged || (subscriber === null || subscriber === void 0 ? void 0 : subscriber.initWithStoredValues) === false);
714
698
  }
715
699
  function hasPendingMergeForKey(key) {
@@ -893,7 +877,9 @@ function subscribeToKey(connectOptions) {
893
877
  }
894
878
  // Commit connection only after init passes
895
879
  deferredInitTask.promise
896
- .then(() => addKeyToRecentlyAccessedIfNeeded(mapping.key))
880
+ // This first .then() adds a microtask tick for compatibility reasons and
881
+ // to ensure subscribers don't receive an extra initial callback before Onyx.update() data arrives.
882
+ .then(() => undefined)
897
883
  .then(() => {
898
884
  // Performance improvement
899
885
  // If the mapping is connected to an onyx key that is not a collection
@@ -1060,7 +1046,7 @@ function setWithRetry({ key, value, options }, retryAttempt) {
1060
1046
  if (value === undefined) {
1061
1047
  return Promise.resolve();
1062
1048
  }
1063
- const existingValue = OnyxCache_1.default.get(key, false);
1049
+ const existingValue = OnyxCache_1.default.get(key);
1064
1050
  // If the existing value as well as the new value are null, we can return early.
1065
1051
  if (existingValue === undefined && value === null) {
1066
1052
  return Promise.resolve();
@@ -1439,7 +1425,6 @@ const OnyxUtils = {
1439
1425
  setSnapshotMergeKeys,
1440
1426
  storeKeyBySubscriptions,
1441
1427
  deleteKeyBySubscriptions,
1442
- addKeyToRecentlyAccessedIfNeeded,
1443
1428
  reduceCollectionWithSelector,
1444
1429
  updateSnapshots,
1445
1430
  mergeCollectionWithPatches,
package/dist/types.d.ts CHANGED
@@ -334,15 +334,9 @@ type InitOptions = {
334
334
  initialKeyStates?: Partial<OnyxInputKeyValueMapping>;
335
335
  /**
336
336
  * This is an array of keys (individual or collection patterns) that when provided to Onyx are flagged
337
- * as "safe" for removal. Any components subscribing to these keys must also implement a canEvict option. See the README for more info.
337
+ * as "safe" for removal.
338
338
  */
339
339
  evictableKeys?: OnyxKey[];
340
- /**
341
- * Sets how many recent keys should we try to keep in cache
342
- * Setting this to 0 would practically mean no cache
343
- * We try to free cache when we connect to a safe eviction key
344
- */
345
- maxCachedKeysCount?: number;
346
340
  /**
347
341
  * Auto synchronize storage events between multiple instances
348
342
  * of Onyx running in different tabs/windows. Defaults to true for platforms that support local storage (web/desktop)
package/dist/useOnyx.d.ts CHANGED
@@ -2,10 +2,6 @@ import type { DependencyList } from 'react';
2
2
  import type { OnyxKey, OnyxValue } from './types';
3
3
  type UseOnyxSelector<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>> = (data: OnyxValue<TKey> | undefined) => TReturnValue;
4
4
  type UseOnyxOptions<TKey extends OnyxKey, TReturnValue> = {
5
- /**
6
- * Determines if this key in this subscription is safe to be evicted.
7
- */
8
- canEvict?: boolean;
9
5
  /**
10
6
  * If set to `false`, then no data will be prefilled into the component.
11
7
  * @deprecated This param is going to be removed soon. Use RAM-only keys instead.
package/dist/useOnyx.js CHANGED
@@ -132,20 +132,6 @@ function useOnyx(key, options, dependencies = []) {
132
132
  onStoreChangeFnRef.current();
133
133
  // eslint-disable-next-line react-hooks/exhaustive-deps
134
134
  }, [...dependencies]);
135
- const checkEvictableKey = (0, react_1.useCallback)(() => {
136
- if ((options === null || options === void 0 ? void 0 : options.canEvict) === undefined || !connectionRef.current) {
137
- return;
138
- }
139
- if (!OnyxCache_1.default.isEvictableKey(key)) {
140
- throw new Error(`canEvict can't be used on key '${key}'. This key must explicitly be flagged as safe for removal by adding it to Onyx.init({evictableKeys: []}).`);
141
- }
142
- if (options.canEvict) {
143
- OnyxConnectionManager_1.default.removeFromEvictionBlockList(connectionRef.current);
144
- }
145
- else {
146
- OnyxConnectionManager_1.default.addToEvictionBlockList(connectionRef.current);
147
- }
148
- }, [key, options === null || options === void 0 ? void 0 : options.canEvict]);
149
135
  // Tracks the last memoizedSelector reference that getSnapshot() has computed with.
150
136
  // When the selector changes, this mismatch forces getSnapshot() to re-evaluate
151
137
  // even if all other conditions (isFirstConnection, shouldGetCachedValue, key) are false.
@@ -262,7 +248,6 @@ function useOnyx(key, options, dependencies = []) {
262
248
  waitForCollectionCallback: OnyxKeys_1.default.isCollectionKey(key),
263
249
  reuseConnection: options === null || options === void 0 ? void 0 : options.reuseConnection,
264
250
  });
265
- checkEvictableKey();
266
251
  return () => {
267
252
  if (!connectionRef.current) {
268
253
  return;
@@ -272,10 +257,7 @@ function useOnyx(key, options, dependencies = []) {
272
257
  isConnectingRef.current = false;
273
258
  onStoreChangeFnRef.current = null;
274
259
  };
275
- }, [key, options === null || options === void 0 ? void 0 : options.initWithStoredValues, options === null || options === void 0 ? void 0 : options.reuseConnection, checkEvictableKey]);
276
- (0, react_1.useEffect)(() => {
277
- checkEvictableKey();
278
- }, [checkEvictableKey]);
260
+ }, [key, options === null || options === void 0 ? void 0 : options.initWithStoredValues, options === null || options === void 0 ? void 0 : options.reuseConnection]);
279
261
  const result = (0, react_1.useSyncExternalStore)(subscribe, getSnapshot);
280
262
  return result;
281
263
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.62",
3
+ "version": "3.0.63",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",