react-native-onyx 2.0.76 → 2.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/Onyx.js CHANGED
@@ -30,7 +30,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
30
30
  const underscore_1 = __importDefault(require("underscore"));
31
31
  const pick_1 = __importDefault(require("lodash/pick"));
32
32
  const Logger = __importStar(require("./Logger"));
33
- const OnyxCache_1 = __importDefault(require("./OnyxCache"));
33
+ const OnyxCache_1 = __importStar(require("./OnyxCache"));
34
34
  const PerformanceUtils = __importStar(require("./PerformanceUtils"));
35
35
  const storage_1 = __importDefault(require("./storage"));
36
36
  const utils_1 = __importDefault(require("./utils"));
@@ -390,7 +390,7 @@ function mergeCollection(collectionKey, collection) {
390
390
  function clear(keysToPreserve = []) {
391
391
  const defaultKeyStates = OnyxUtils_1.default.getDefaultKeyStates();
392
392
  const initialKeys = Object.keys(defaultKeyStates);
393
- return OnyxUtils_1.default.getAllKeys()
393
+ const promise = OnyxUtils_1.default.getAllKeys()
394
394
  .then((cachedKeys) => {
395
395
  OnyxCache_1.default.clearNullishStorageKeys();
396
396
  const keysToBeClearedFromStorage = [];
@@ -459,6 +459,7 @@ function clear(keysToPreserve = []) {
459
459
  // Remove only the items that we want cleared from storage, and reset others to default
460
460
  keysToBeClearedFromStorage.forEach((key) => OnyxCache_1.default.drop(key));
461
461
  return storage_1.default.removeItems(keysToBeClearedFromStorage)
462
+ .then(() => OnyxConnectionManager_1.default.refreshSessionID())
462
463
  .then(() => storage_1.default.multiSet(defaultKeyValuePairs))
463
464
  .then(() => {
464
465
  DevTools_1.default.clearState(keysToPreserve);
@@ -466,6 +467,7 @@ function clear(keysToPreserve = []) {
466
467
  });
467
468
  })
468
469
  .then(() => undefined);
470
+ return OnyxCache_1.default.captureTask(OnyxCache_1.TASK.CLEAR, promise);
469
471
  }
470
472
  function updateSnapshots(data) {
471
473
  const snapshotCollectionKey = OnyxUtils_1.default.getSnapshotKey();
@@ -1,4 +1,11 @@
1
+ import type { ValueOf } from 'type-fest';
1
2
  import type { OnyxKey, OnyxValue } from './types';
3
+ declare const TASK: {
4
+ readonly GET: "get";
5
+ readonly GET_ALL_KEYS: "getAllKeys";
6
+ readonly CLEAR: "clear";
7
+ };
8
+ type CacheTask = ValueOf<typeof TASK> | `${ValueOf<typeof TASK>}:${string}`;
2
9
  /**
3
10
  * In memory cache providing data by reference
4
11
  * Encapsulates Onyx cache related functionality
@@ -67,20 +74,20 @@ declare class OnyxCache {
67
74
  * Check whether the given task is already running
68
75
  * @param taskName - unique name given for the task
69
76
  */
70
- hasPendingTask(taskName: string): boolean;
77
+ hasPendingTask(taskName: CacheTask): boolean;
71
78
  /**
72
79
  * Use this method to prevent concurrent calls for the same thing
73
80
  * Instead of calling the same task again use the existing promise
74
81
  * provided from this function
75
82
  * @param taskName - unique name given for the task
76
83
  */
77
- getTaskPromise(taskName: string): Promise<OnyxValue<OnyxKey> | OnyxKey[]> | undefined;
84
+ getTaskPromise(taskName: CacheTask): Promise<OnyxValue<OnyxKey> | OnyxKey[]> | undefined;
78
85
  /**
79
86
  * Capture a promise for a given task so other caller can
80
87
  * hook up to the promise if it's still pending
81
88
  * @param taskName - unique name for the task
82
89
  */
83
- captureTask(taskName: string, promise: Promise<OnyxValue<OnyxKey>>): Promise<OnyxValue<OnyxKey>>;
90
+ captureTask(taskName: CacheTask, promise: Promise<OnyxValue<OnyxKey>>): Promise<OnyxValue<OnyxKey>>;
84
91
  /** Adds a key to the top of the recently accessed keys */
85
92
  addToAccessedKeys(key: OnyxKey): void;
86
93
  /** Remove keys that don't fall into the range of recently used keys */
@@ -92,3 +99,5 @@ declare class OnyxCache {
92
99
  }
93
100
  declare const instance: OnyxCache;
94
101
  export default instance;
102
+ export { TASK };
103
+ export type { CacheTask };
package/dist/OnyxCache.js CHANGED
@@ -3,9 +3,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TASK = void 0;
6
7
  const fast_equals_1 = require("fast-equals");
7
8
  const bindAll_1 = __importDefault(require("lodash/bindAll"));
8
9
  const utils_1 = __importDefault(require("./utils"));
10
+ // Task constants
11
+ const TASK = {
12
+ GET: 'get',
13
+ GET_ALL_KEYS: 'getAllKeys',
14
+ CLEAR: 'clear',
15
+ };
16
+ exports.TASK = TASK;
9
17
  /**
10
18
  * In memory cache providing data by reference
11
19
  * Encapsulates Onyx cache related functionality
@@ -25,6 +25,20 @@ declare class OnyxConnectionManager {
25
25
  * Stores the last generated callback ID which will be incremented when making a new connection.
26
26
  */
27
27
  private lastCallbackID;
28
+ /**
29
+ * Stores the last generated session ID for the connection manager. The current session ID
30
+ * is appended to the connection IDs and it's used to create new different connections for the same key
31
+ * when `refreshSessionID()` is called.
32
+ *
33
+ * When calling `Onyx.clear()` after a logout operation some connections might remain active as they
34
+ * aren't tied to the React's lifecycle e.g. `Onyx.connect()` usage, causing infinite loading state issues to new `useOnyx()` subscribers
35
+ * that are connecting to the same key as we didn't populate the cache again because we are still reusing such connections.
36
+ *
37
+ * To elimitate this problem, the session ID must be refreshed during the `Onyx.clear()` call (by using `refreshSessionID()`)
38
+ * in order to create fresh connections when new subscribers connect to the same keys again, allowing them
39
+ * to use the cache system correctly and avoid the mentioned issues in `useOnyx()`.
40
+ */
41
+ private sessionID;
28
42
  constructor();
29
43
  /**
30
44
  * Generates a connection ID based on the `connectOptions` object passed to the function.
@@ -54,6 +68,10 @@ declare class OnyxConnectionManager {
54
68
  * Disconnect all subscribers from Onyx.
55
69
  */
56
70
  disconnectAll(): void;
71
+ /**
72
+ * Refreshes the connection manager's session ID.
73
+ */
74
+ refreshSessionID(): void;
57
75
  /**
58
76
  * Adds the connection to the eviction block list. Connections added to this list can never be evicted.
59
77
  * */
@@ -38,8 +38,9 @@ class OnyxConnectionManager {
38
38
  constructor() {
39
39
  this.connectionsMap = new Map();
40
40
  this.lastCallbackID = 0;
41
+ this.sessionID = Str.guid();
41
42
  // Binds all public methods to prevent problems with `this`.
42
- (0, bindAll_1.default)(this, 'generateConnectionID', 'fireCallbacks', 'connect', 'disconnect', 'disconnectAll', 'addToEvictionBlockList', 'removeFromEvictionBlockList');
43
+ (0, bindAll_1.default)(this, 'generateConnectionID', 'fireCallbacks', 'connect', 'disconnect', 'disconnectAll', 'refreshSessionID', 'addToEvictionBlockList', 'removeFromEvictionBlockList');
43
44
  }
44
45
  /**
45
46
  * Generates a connection ID based on the `connectOptions` object passed to the function.
@@ -49,7 +50,9 @@ class OnyxConnectionManager {
49
50
  */
50
51
  generateConnectionID(connectOptions) {
51
52
  const { key, initWithStoredValues, reuseConnection, waitForCollectionCallback } = connectOptions;
52
- let suffix = '';
53
+ // The current session ID is appended to the connection ID so we can have different connections
54
+ // after an `Onyx.clear()` operation.
55
+ let suffix = `,sessionID=${this.sessionID}`;
53
56
  // We will generate a unique ID in any of the following situations:
54
57
  // - `reuseConnection` is `false`. That means the subscriber explicitly wants the connection to not be reused.
55
58
  // - `initWithStoredValues` is `false`. This flag changes the subscription flow when set to `false`, so the connection can't be reused.
@@ -162,6 +165,12 @@ class OnyxConnectionManager {
162
165
  });
163
166
  this.connectionsMap.clear();
164
167
  }
168
+ /**
169
+ * Refreshes the connection manager's session ID.
170
+ */
171
+ refreshSessionID() {
172
+ this.sessionID = Str.guid();
173
+ }
165
174
  /**
166
175
  * Adds the connection to the eviction block list. Connections added to this list can never be evicted.
167
176
  * */
package/dist/OnyxUtils.js CHANGED
@@ -32,7 +32,7 @@ const fast_equals_1 = require("fast-equals");
32
32
  const clone_1 = __importDefault(require("lodash/clone"));
33
33
  const DevTools_1 = __importDefault(require("./DevTools"));
34
34
  const Logger = __importStar(require("./Logger"));
35
- const OnyxCache_1 = __importDefault(require("./OnyxCache"));
35
+ const OnyxCache_1 = __importStar(require("./OnyxCache"));
36
36
  const PerformanceUtils = __importStar(require("./PerformanceUtils"));
37
37
  const Str = __importStar(require("./Str"));
38
38
  const batch_1 = __importDefault(require("./batch"));
@@ -187,7 +187,7 @@ function get(key) {
187
187
  if (OnyxCache_1.default.hasCacheForKey(key)) {
188
188
  return Promise.resolve(OnyxCache_1.default.get(key));
189
189
  }
190
- const taskName = `get:${key}`;
190
+ const taskName = `${OnyxCache_1.TASK.GET}:${key}`;
191
191
  // When a value retrieving task for this key is still running hook to it
192
192
  if (OnyxCache_1.default.hasPendingTask(taskName)) {
193
193
  return OnyxCache_1.default.getTaskPromise(taskName);
@@ -229,7 +229,7 @@ function multiGet(keys) {
229
229
  dataMap.set(key, cacheValue);
230
230
  return;
231
231
  }
232
- const pendingKey = `get:${key}`;
232
+ const pendingKey = `${OnyxCache_1.TASK.GET}:${key}`;
233
233
  if (OnyxCache_1.default.hasPendingTask(pendingKey)) {
234
234
  pendingTasks.push(OnyxCache_1.default.getTaskPromise(pendingKey));
235
235
  pendingKeys.push(key);
@@ -300,10 +300,9 @@ function getAllKeys() {
300
300
  if (cachedKeys.size > 0) {
301
301
  return Promise.resolve(cachedKeys);
302
302
  }
303
- const taskName = 'getAllKeys';
304
303
  // When a value retrieving task for all keys is still running hook to it
305
- if (OnyxCache_1.default.hasPendingTask(taskName)) {
306
- return OnyxCache_1.default.getTaskPromise(taskName);
304
+ if (OnyxCache_1.default.hasPendingTask(OnyxCache_1.TASK.GET_ALL_KEYS)) {
305
+ return OnyxCache_1.default.getTaskPromise(OnyxCache_1.TASK.GET_ALL_KEYS);
307
306
  }
308
307
  // Otherwise retrieve the keys from storage and capture a promise to aid concurrent usages
309
308
  const promise = storage_1.default.getAllKeys().then((keys) => {
@@ -311,7 +310,7 @@ function getAllKeys() {
311
310
  // return the updated set of keys
312
311
  return OnyxCache_1.default.getAllKeys();
313
312
  });
314
- return OnyxCache_1.default.captureTask(taskName, promise);
313
+ return OnyxCache_1.default.captureTask(OnyxCache_1.TASK.GET_ALL_KEYS, promise);
315
314
  }
316
315
  /**
317
316
  * Returns set of all registered collection keys
package/dist/useOnyx.d.ts CHANGED
@@ -12,6 +12,11 @@ type BaseUseOnyxOptions = {
12
12
  * If set to `true`, data will be retrieved from cache during the first render even if there is a pending merge for the key.
13
13
  */
14
14
  allowStaleData?: boolean;
15
+ /**
16
+ * If set to `false`, the connection won't be reused between other subscribers that are listening to the same Onyx key
17
+ * with the same connect configurations.
18
+ */
19
+ reuseConnection?: boolean;
15
20
  };
16
21
  type UseOnyxInitialValueOption<TInitialValue> = {
17
22
  /**
package/dist/useOnyx.js CHANGED
@@ -1,11 +1,34 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
29
  const fast_equals_1 = require("fast-equals");
7
30
  const react_1 = require("react");
8
- const OnyxCache_1 = __importDefault(require("./OnyxCache"));
31
+ const OnyxCache_1 = __importStar(require("./OnyxCache"));
9
32
  const OnyxConnectionManager_1 = __importDefault(require("./OnyxConnectionManager"));
10
33
  const OnyxUtils_1 = __importDefault(require("./OnyxUtils"));
11
34
  const useLiveRef_1 = __importDefault(require("./useLiveRef"));
@@ -144,11 +167,14 @@ function useOnyx(key, options) {
144
167
  else {
145
168
  areValuesEqual = (0, fast_equals_1.shallowEqual)((_c = previousValueRef.current) !== null && _c !== void 0 ? _c : undefined, newValueRef.current);
146
169
  }
147
- // If the previously cached value is different from the new value, we update both cached value
148
- // and the result to be returned by the hook.
149
- // If the cache was set for the first time, we also update the cached value and the result.
150
- const isCacheSetFirstTime = previousValueRef.current === null && hasCacheForKey;
151
- if (isCacheSetFirstTime || !areValuesEqual) {
170
+ // We updated the cached value and the result in the following conditions:
171
+ // We will update the cached value and the result in any of the following situations:
172
+ // - The previously cached value is different from the new value.
173
+ // - The previously cached value is `null` (not set from cache yet) and we have cache for this key
174
+ // OR we have a pending `Onyx.clear()` task (if `Onyx.clear()` is running cache might not be available anymore
175
+ // so we update the cached value/result right away in order to prevent infinite loading state issues).
176
+ const shouldUpdateResult = !areValuesEqual || (previousValueRef.current === null && (hasCacheForKey || OnyxCache_1.default.hasPendingTask(OnyxCache_1.TASK.CLEAR)));
177
+ if (shouldUpdateResult) {
152
178
  previousValueRef.current = newValueRef.current;
153
179
  // If the new value is `null` we default it to `undefined` to ensure the consumer gets a consistent result from the hook.
154
180
  resultRef.current = [(_d = previousValueRef.current) !== null && _d !== void 0 ? _d : undefined, { status: newFetchStatus !== null && newFetchStatus !== void 0 ? newFetchStatus : 'loaded' }];
@@ -169,6 +195,7 @@ function useOnyx(key, options) {
169
195
  },
170
196
  initWithStoredValues: options === null || options === void 0 ? void 0 : options.initWithStoredValues,
171
197
  waitForCollectionCallback: OnyxUtils_1.default.isCollectionKey(key),
198
+ reuseConnection: options === null || options === void 0 ? void 0 : options.reuseConnection,
172
199
  });
173
200
  checkEvictableKey();
174
201
  return () => {
@@ -178,7 +205,7 @@ function useOnyx(key, options) {
178
205
  OnyxConnectionManager_1.default.disconnect(connectionRef.current);
179
206
  isFirstConnectionRef.current = false;
180
207
  };
181
- }, [key, options === null || options === void 0 ? void 0 : options.initWithStoredValues, checkEvictableKey]);
208
+ }, [key, options === null || options === void 0 ? void 0 : options.initWithStoredValues, options === null || options === void 0 ? void 0 : options.reuseConnection, checkEvictableKey]);
182
209
  (0, react_1.useEffect)(() => {
183
210
  checkEvictableKey();
184
211
  }, [checkEvictableKey]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "2.0.76",
3
+ "version": "2.0.78",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",