react-native-onyx 2.0.54 → 2.0.55

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
@@ -97,6 +97,10 @@ function connect(connectOptions) {
97
97
  const callbackToStateMapping = OnyxUtils_1.default.getCallbackToStateMapping();
98
98
  callbackToStateMapping[connectionID] = mapping;
99
99
  callbackToStateMapping[connectionID].connectionID = connectionID;
100
+ // When keyChanged is called, a key is passed and the method looks through all the Subscribers in callbackToStateMapping for the matching key to get the connectionID
101
+ // to avoid having to loop through all the Subscribers all the time (even when just one connection belongs to one key),
102
+ // We create a mapping from key to lists of connectionIDs to access the specific list of connectionIDs.
103
+ OnyxUtils_1.default.storeKeyByConnections(mapping.key, callbackToStateMapping[connectionID].connectionID);
100
104
  if (mapping.initWithStoredValues === false) {
101
105
  return connectionID;
102
106
  }
@@ -191,6 +195,7 @@ function disconnect(connectionID, keyToRemoveFromEvictionBlocklist) {
191
195
  if (keyToRemoveFromEvictionBlocklist) {
192
196
  OnyxUtils_1.default.removeFromEvictionBlockList(keyToRemoveFromEvictionBlocklist, connectionID);
193
197
  }
198
+ OnyxUtils_1.default.deleteKeyByConnections(lastConnectionID);
194
199
  delete callbackToStateMapping[connectionID];
195
200
  }
196
201
  /**
@@ -55,6 +55,19 @@ declare function batchUpdates(updates: () => void): Promise<void>;
55
55
  /** Get some data from the store */
56
56
  declare function get<TKey extends OnyxKey, TValue extends OnyxValue<TKey>>(key: TKey): Promise<TValue>;
57
57
  declare function multiGet<TKey extends OnyxKey>(keys: CollectionKeyBase[]): Promise<Map<OnyxKey, OnyxValue<TKey>>>;
58
+ /**
59
+ * Stores a connection ID associated with a given key.
60
+ *
61
+ * @param connectionID - a connection ID of the subscriber
62
+ * @param key - a key that the subscriber is connected to
63
+ */
64
+ declare function storeKeyByConnections(key: OnyxKey, connectionID: number): void;
65
+ /**
66
+ * Deletes a connection ID associated with its corresponding key.
67
+ *
68
+ * @param {number} connectionID - The connection ID to be deleted.
69
+ */
70
+ declare function deleteKeyByConnections(connectionID: number): void;
58
71
  /** Returns current key names stored in persisted storage */
59
72
  declare function getAllKeys(): Promise<Set<OnyxKey>>;
60
73
  /**
@@ -76,6 +89,18 @@ declare function splitCollectionMemberKey<TKey extends CollectionKey>(key: TKey)
76
89
  declare function isKeyMatch(configKey: OnyxKey, key: OnyxKey): boolean;
77
90
  /** Checks to see if this key has been flagged as safe for removal. */
78
91
  declare function isSafeEvictionKey(testKey: OnyxKey): boolean;
92
+ /**
93
+ * It extracts the non-numeric collection identifier of a given key.
94
+ *
95
+ * For example:
96
+ * - `getCollectionKey("report_123")` would return "report_"
97
+ * - `getCollectionKey("report")` would return "report"
98
+ * - `getCollectionKey("report_")` would return "report_"
99
+ *
100
+ * @param {OnyxKey} key - The key to process.
101
+ * @return {string} The pure key without any numeric
102
+ */
103
+ declare function getCollectionKey(key: OnyxKey): string;
79
104
  /**
80
105
  * Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
81
106
  * If the requested key is a collection, it will return an object with all the collection members.
@@ -225,6 +250,7 @@ declare const OnyxUtils: {
225
250
  keyChanged: typeof keyChanged;
226
251
  sendDataToConnection: typeof sendDataToConnection;
227
252
  addKeyToRecentlyAccessedIfNeeded: typeof addKeyToRecentlyAccessedIfNeeded;
253
+ getCollectionKey: typeof getCollectionKey;
228
254
  getCollectionDataAndSendAsObject: typeof getCollectionDataAndSendAsObject;
229
255
  scheduleSubscriberUpdate: typeof scheduleSubscriberUpdate;
230
256
  scheduleNotifyCollectionSubscribers: typeof scheduleNotifyCollectionSubscribers;
@@ -237,6 +263,8 @@ declare const OnyxUtils: {
237
263
  prepareKeyValuePairsForStorage: typeof prepareKeyValuePairsForStorage;
238
264
  applyMerge: typeof applyMerge;
239
265
  initializeWithDefaultKeyStates: typeof initializeWithDefaultKeyStates;
266
+ storeKeyByConnections: typeof storeKeyByConnections;
267
+ deleteKeyByConnections: typeof deleteKeyByConnections;
240
268
  getSnapshotKey: typeof getSnapshotKey;
241
269
  multiGet: typeof multiGet;
242
270
  };
package/dist/OnyxUtils.js CHANGED
@@ -53,6 +53,8 @@ const mergeQueuePromise = {};
53
53
  const callbackToStateMapping = {};
54
54
  // Keeps a copy of the values of the onyx collection keys as a map for faster lookups
55
55
  let onyxCollectionKeyMap = new Map();
56
+ // Holds a mapping of the connected key to the connectionID for faster lookups
57
+ const onyxKeyToConnectionIDs = new Map();
56
58
  // Holds a list of keys that have been directly subscribed to or recently modified from least to most recent
57
59
  let recentlyAccessedKeys = [];
58
60
  // Holds a list of keys that are safe to remove when we reach max storage. If a key does not match with
@@ -253,6 +255,30 @@ function multiGet(keys) {
253
255
  return dataMap;
254
256
  }));
255
257
  }
258
+ /**
259
+ * Stores a connection ID associated with a given key.
260
+ *
261
+ * @param connectionID - a connection ID of the subscriber
262
+ * @param key - a key that the subscriber is connected to
263
+ */
264
+ function storeKeyByConnections(key, connectionID) {
265
+ if (!onyxKeyToConnectionIDs.has(key)) {
266
+ onyxKeyToConnectionIDs.set(key, []);
267
+ }
268
+ onyxKeyToConnectionIDs.get(key).push(connectionID);
269
+ }
270
+ /**
271
+ * Deletes a connection ID associated with its corresponding key.
272
+ *
273
+ * @param {number} connectionID - The connection ID to be deleted.
274
+ */
275
+ function deleteKeyByConnections(connectionID) {
276
+ const subscriber = callbackToStateMapping[connectionID];
277
+ if (subscriber && onyxKeyToConnectionIDs.has(subscriber.key)) {
278
+ const updatedConnectionIDs = onyxKeyToConnectionIDs.get(subscriber.key).filter((id) => id !== connectionID);
279
+ onyxKeyToConnectionIDs.set(subscriber.key, updatedConnectionIDs);
280
+ }
281
+ }
256
282
  /** Returns current key names stored in persisted storage */
257
283
  function getAllKeys() {
258
284
  // When we've already read stored keys, resolve right away
@@ -289,7 +315,7 @@ function isCollectionMemberKey(collectionKey, key) {
289
315
  * @returns A tuple where the first element is the collection part and the second element is the ID part.
290
316
  */
291
317
  function splitCollectionMemberKey(key) {
292
- const underscoreIndex = key.indexOf('_');
318
+ const underscoreIndex = key.lastIndexOf('_');
293
319
  if (underscoreIndex === -1) {
294
320
  throw new Error(`Invalid ${key} key provided, only collection keys are allowed.`);
295
321
  }
@@ -306,6 +332,24 @@ function isKeyMatch(configKey, key) {
306
332
  function isSafeEvictionKey(testKey) {
307
333
  return evictionAllowList.some((key) => isKeyMatch(key, testKey));
308
334
  }
335
+ /**
336
+ * It extracts the non-numeric collection identifier of a given key.
337
+ *
338
+ * For example:
339
+ * - `getCollectionKey("report_123")` would return "report_"
340
+ * - `getCollectionKey("report")` would return "report"
341
+ * - `getCollectionKey("report_")` would return "report_"
342
+ *
343
+ * @param {OnyxKey} key - The key to process.
344
+ * @return {string} The pure key without any numeric
345
+ */
346
+ function getCollectionKey(key) {
347
+ const underscoreIndex = key.lastIndexOf('_');
348
+ if (underscoreIndex === -1) {
349
+ return key;
350
+ }
351
+ return key.substring(0, underscoreIndex + 1);
352
+ }
309
353
  /**
310
354
  * Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
311
355
  * If the requested key is a collection, it will return an object with all the collection members.
@@ -576,6 +620,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
576
620
  * keyChanged(key, value, subscriber => subscriber.initWithStoredValues === false)
577
621
  */
578
622
  function keyChanged(key, value, previousValue, canUpdateSubscriber = () => true, notifyRegularSubscibers = true, notifyWithOnyxSubscibers = true) {
623
+ var _a, _b;
579
624
  // Add or remove this key from the recentlyAccessedKeys lists
580
625
  if (value !== null) {
581
626
  addLastAccessedKey(key);
@@ -583,10 +628,22 @@ function keyChanged(key, value, previousValue, canUpdateSubscriber = () => true,
583
628
  else {
584
629
  removeLastAccessedKey(key);
585
630
  }
586
- // We are iterating over all subscribers to see if they are interested in the key that has just changed. If the subscriber's key is a collection key then we will
631
+ // We get the subscribers interested in the key that has just changed. If the subscriber's key is a collection key then we will
587
632
  // notify them if the key that changed is a collection member. Or if it is a regular key notify them when there is an exact match. Depending on whether the subscriber
588
633
  // was connected via withOnyx we will call setState() directly on the withOnyx instance. If it is a regular connection we will pass the data to the provided callback.
589
- const stateMappingKeys = Object.keys(callbackToStateMapping);
634
+ // Given the amount of times this function is called we need to make sure we are not iterating over all subscribers every time. On the other hand, we don't need to
635
+ // do the same in keysChanged, because we only call that function when a collection key changes, and it doesn't happen that often.
636
+ // For performance reason, we look for the given key and later if don't find it we look for the collection key, instead of checking if it is a collection key first.
637
+ let stateMappingKeys = (_a = onyxKeyToConnectionIDs.get(key)) !== null && _a !== void 0 ? _a : [];
638
+ const collectionKey = getCollectionKey(key);
639
+ const plainCollectionKey = collectionKey.lastIndexOf('_') !== -1 ? collectionKey : undefined;
640
+ if (plainCollectionKey) {
641
+ // Getting the collection key from the specific key because only collection keys were stored in the mapping.
642
+ stateMappingKeys = [...stateMappingKeys, ...((_b = onyxKeyToConnectionIDs.get(plainCollectionKey)) !== null && _b !== void 0 ? _b : [])];
643
+ if (stateMappingKeys.length === 0) {
644
+ return;
645
+ }
646
+ }
590
647
  for (let i = 0; i < stateMappingKeys.length; i++) {
591
648
  const subscriber = callbackToStateMapping[stateMappingKeys[i]];
592
649
  if (!subscriber || !isKeyMatch(subscriber.key, key) || !canUpdateSubscriber(subscriber)) {
@@ -926,6 +983,7 @@ const OnyxUtils = {
926
983
  keyChanged,
927
984
  sendDataToConnection,
928
985
  addKeyToRecentlyAccessedIfNeeded,
986
+ getCollectionKey,
929
987
  getCollectionDataAndSendAsObject,
930
988
  scheduleSubscriberUpdate,
931
989
  scheduleNotifyCollectionSubscribers,
@@ -938,6 +996,8 @@ const OnyxUtils = {
938
996
  prepareKeyValuePairsForStorage,
939
997
  applyMerge,
940
998
  initializeWithDefaultKeyStates,
999
+ storeKeyByConnections,
1000
+ deleteKeyByConnections,
941
1001
  getSnapshotKey,
942
1002
  multiGet,
943
1003
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "2.0.54",
3
+ "version": "2.0.55",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",