react-native-onyx 3.0.58 → 3.0.60

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.
@@ -12,7 +12,7 @@ const OnyxKeys_1 = __importDefault(require("./OnyxKeys"));
12
12
  class OnyxSnapshotCache {
13
13
  constructor() {
14
14
  this.snapshotCache = new Map();
15
- this.selectorIDMap = new Map();
15
+ this.selectorIDMap = new WeakMap();
16
16
  this.selectorIDCounter = 0;
17
17
  this.cacheKeyRefCounts = new Map();
18
18
  }
package/dist/useOnyx.js CHANGED
@@ -42,12 +42,10 @@ const OnyxCache_1 = __importStar(require("./OnyxCache"));
42
42
  const OnyxConnectionManager_1 = __importDefault(require("./OnyxConnectionManager"));
43
43
  const OnyxUtils_1 = __importDefault(require("./OnyxUtils"));
44
44
  const OnyxKeys_1 = __importDefault(require("./OnyxKeys"));
45
- const usePrevious_1 = __importDefault(require("./usePrevious"));
46
45
  const OnyxSnapshotCache_1 = __importDefault(require("./OnyxSnapshotCache"));
47
46
  const useLiveRef_1 = __importDefault(require("./useLiveRef"));
48
47
  function useOnyx(key, options, dependencies = []) {
49
48
  const connectionRef = (0, react_1.useRef)(null);
50
- const previousKey = (0, usePrevious_1.default)(key);
51
49
  const currentDependenciesRef = (0, useLiveRef_1.default)(dependencies);
52
50
  const selector = options === null || options === void 0 ? void 0 : options.selector;
53
51
  // Create memoized version of selector for performance
@@ -93,9 +91,12 @@ function useOnyx(key, options, dependencies = []) {
93
91
  status: (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false ? 'loaded' : 'loading',
94
92
  },
95
93
  ]);
96
- // Indicates if it's the first Onyx connection of this hook or not, as we don't want certain use cases
97
- // in `getSnapshot()` to be satisfied several times.
98
- const isFirstConnectionRef = (0, react_1.useRef)(true);
94
+ // Tracks which key has completed its first Onyx connection callback. When this doesn't match the
95
+ // current key, getSnapshot() treats the hook as being in its "first connection" state for that key.
96
+ // This is key-aware by design: when the key changes, connectedKeyRef still holds the old key (or null
97
+ // after cleanup), so the hook automatically enters first-connection mode for the new key without any
98
+ // explicit reset logic — eliminating the race condition where cleanup could clobber a boolean flag.
99
+ const connectedKeyRef = (0, react_1.useRef)(null);
99
100
  // Indicates if the hook is connecting to an Onyx key.
100
101
  const isConnectingRef = (0, react_1.useRef)(false);
101
102
  // Stores the `onStoreChange()` function, which can be used to trigger a `getSnapshot()` update when desired.
@@ -122,7 +123,7 @@ function useOnyx(key, options, dependencies = []) {
122
123
  return;
123
124
  }
124
125
  previousDependenciesRef.current = dependencies;
125
- if (connectionRef.current === null || isConnectingRef.current || !onStoreChangeFnRef.current) {
126
+ if (connectionRef.current === null || isConnectingRef.current || connectedKeyRef.current !== key || !onStoreChangeFnRef.current) {
126
127
  return;
127
128
  }
128
129
  // Invalidate cache when dependencies change so selector runs with new closure values
@@ -154,7 +155,8 @@ function useOnyx(key, options, dependencies = []) {
154
155
  // Check if we have any cache for this Onyx key
155
156
  // Don't use cache for first connection with initWithStoredValues: false
156
157
  // Also don't use cache during active data updates (when shouldGetCachedValueRef is true)
157
- if (!(isFirstConnectionRef.current && (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false) && !shouldGetCachedValueRef.current) {
158
+ const isFirstConnection = connectedKeyRef.current !== key;
159
+ if (!(isFirstConnection && (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false) && !shouldGetCachedValueRef.current) {
158
160
  const cachedResult = OnyxSnapshotCache_1.default.getCachedResult(key, cacheKey);
159
161
  if (cachedResult !== undefined) {
160
162
  resultRef.current = cachedResult;
@@ -162,7 +164,7 @@ function useOnyx(key, options, dependencies = []) {
162
164
  }
163
165
  }
164
166
  // We return the initial result right away during the first connection if `initWithStoredValues` is set to `false`.
165
- if (isFirstConnectionRef.current && (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false) {
167
+ if (isFirstConnection && (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false) {
166
168
  const result = resultRef.current;
167
169
  // Store result in snapshot cache
168
170
  OnyxSnapshotCache_1.default.setCachedResult(key, cacheKey, result);
@@ -172,7 +174,7 @@ function useOnyx(key, options, dependencies = []) {
172
174
  // so we can return any cached value right away. For the case where the key has changed, If we don't return the cached value right away, then the UI will show the incorrect (previous) value for a brief period which looks like a UI glitch to the user. After the connection is made, we only
173
175
  // update `newValueRef` when `Onyx.connect()` callback is fired.
174
176
  const hasSelectorChanged = lastComputedSelectorRef.current !== memoizedSelector;
175
- if (isFirstConnectionRef.current || shouldGetCachedValueRef.current || key !== previousKey || hasSelectorChanged) {
177
+ if (isFirstConnection || shouldGetCachedValueRef.current || hasSelectorChanged) {
176
178
  // Gets the value from cache and maps it with selector. It changes `null` to `undefined` for `useOnyx` compatibility.
177
179
  const value = OnyxUtils_1.default.tryGetCachedValue(key);
178
180
  const selectedValue = memoizedSelector ? memoizedSelector(value) : value;
@@ -187,7 +189,7 @@ function useOnyx(key, options, dependencies = []) {
187
189
  let newFetchStatus;
188
190
  // If we have pending merge operations for the key during the first connection, we set the new value to `undefined`
189
191
  // and fetch status to `loading` to simulate that it is still being loaded until we have the most updated data.
190
- if (isFirstConnectionRef.current && OnyxUtils_1.default.hasPendingMergeForKey(key)) {
192
+ if (isFirstConnection && OnyxUtils_1.default.hasPendingMergeForKey(key)) {
191
193
  newValueRef.current = undefined;
192
194
  newFetchStatus = 'loading';
193
195
  }
@@ -211,7 +213,7 @@ function useOnyx(key, options, dependencies = []) {
211
213
  // OR we have a pending `Onyx.clear()` task (if `Onyx.clear()` is running cache might not be available anymore
212
214
  // OR the subscriber is triggered (the value is gotten from the storage)
213
215
  // so we update the cached value/result right away in order to prevent infinite loading state issues).
214
- const shouldUpdateResult = !areValuesEqual || (previousValueRef.current === null && (hasCacheForKey || OnyxCache_1.default.hasPendingTask(OnyxCache_1.TASK.CLEAR) || !isFirstConnectionRef.current));
216
+ const shouldUpdateResult = !areValuesEqual || (previousValueRef.current === null && (hasCacheForKey || OnyxCache_1.default.hasPendingTask(OnyxCache_1.TASK.CLEAR) || !isFirstConnection));
215
217
  if (shouldUpdateResult) {
216
218
  previousValueRef.current = newValueRef.current;
217
219
  // If the new value is `null` we default it to `undefined` to ensure the consumer gets a consistent result from the hook.
@@ -228,8 +230,15 @@ function useOnyx(key, options, dependencies = []) {
228
230
  OnyxSnapshotCache_1.default.setCachedResult(key, cacheKey, resultRef.current);
229
231
  }
230
232
  return resultRef.current;
231
- }, [options === null || options === void 0 ? void 0 : options.initWithStoredValues, key, memoizedSelector, cacheKey, previousKey]);
233
+ }, [options === null || options === void 0 ? void 0 : options.initWithStoredValues, key, memoizedSelector, cacheKey]);
232
234
  const subscribe = (0, react_1.useCallback)((onStoreChange) => {
235
+ // Reset internal state so the hook properly transitions through loading
236
+ // for the new key instead of preserving stale state from the previous one.
237
+ previousValueRef.current = null;
238
+ newValueRef.current = null;
239
+ shouldGetCachedValueRef.current = true;
240
+ sourceValueRef.current = undefined;
241
+ resultRef.current = [undefined, { status: (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false ? 'loaded' : 'loading' }];
233
242
  isConnectingRef.current = true;
234
243
  onStoreChangeFnRef.current = onStoreChange;
235
244
  connectionRef.current = OnyxConnectionManager_1.default.connect({
@@ -237,9 +246,9 @@ function useOnyx(key, options, dependencies = []) {
237
246
  callback: (value, callbackKey, sourceValue) => {
238
247
  isConnectingRef.current = false;
239
248
  onStoreChangeFnRef.current = onStoreChange;
240
- // Signals that the first connection was made, so some logics in `getSnapshot()`
241
- // won't be executed anymore.
242
- isFirstConnectionRef.current = false;
249
+ // Signals that the first connection was made for this key, so some logics
250
+ // in `getSnapshot()` won't be executed anymore.
251
+ connectedKeyRef.current = key;
243
252
  // Signals that we want to get the newest cached value again in `getSnapshot()`.
244
253
  shouldGetCachedValueRef.current = true;
245
254
  // sourceValue is unknown type, so we need to cast it to the correct type.
@@ -259,7 +268,7 @@ function useOnyx(key, options, dependencies = []) {
259
268
  return;
260
269
  }
261
270
  OnyxConnectionManager_1.default.disconnect(connectionRef.current);
262
- isFirstConnectionRef.current = false;
271
+ connectedKeyRef.current = null;
263
272
  isConnectingRef.current = false;
264
273
  onStoreChangeFnRef.current = null;
265
274
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.58",
3
+ "version": "3.0.60",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",
@@ -1,5 +0,0 @@
1
- /**
2
- * Returns the previous value of the provided value.
3
- */
4
- declare function usePrevious<T>(value: T): T;
5
- export default usePrevious;
@@ -1,14 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const react_1 = require("react");
4
- /**
5
- * Returns the previous value of the provided value.
6
- */
7
- function usePrevious(value) {
8
- const ref = (0, react_1.useRef)(value);
9
- (0, react_1.useEffect)(() => {
10
- ref.current = value;
11
- }, [value]);
12
- return ref.current;
13
- }
14
- exports.default = usePrevious;