react-native-onyx 2.0.134 → 2.0.136
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/OnyxConnectionManager.js +5 -0
- package/dist/OnyxSnapshotCache.d.ts +78 -0
- package/dist/OnyxSnapshotCache.js +130 -0
- package/dist/OnyxUtils.d.ts +2 -2
- package/dist/OnyxUtils.js +10 -8
- package/dist/types.d.ts +2 -0
- package/dist/useOnyx.d.ts +1 -1
- package/dist/useOnyx.js +31 -3
- package/package.json +1 -1
|
@@ -42,6 +42,7 @@ const OnyxUtils_1 = __importDefault(require("./OnyxUtils"));
|
|
|
42
42
|
const Str = __importStar(require("./Str"));
|
|
43
43
|
const utils_1 = __importDefault(require("./utils"));
|
|
44
44
|
const OnyxCache_1 = __importDefault(require("./OnyxCache"));
|
|
45
|
+
const OnyxSnapshotCache_1 = __importDefault(require("./OnyxSnapshotCache"));
|
|
45
46
|
/**
|
|
46
47
|
* Manages Onyx connections of `Onyx.connect()`, `useOnyx()` and `withOnyx()` subscribers.
|
|
47
48
|
*/
|
|
@@ -182,12 +183,16 @@ class OnyxConnectionManager {
|
|
|
182
183
|
});
|
|
183
184
|
});
|
|
184
185
|
this.connectionsMap.clear();
|
|
186
|
+
// Clear snapshot cache when all connections are disconnected
|
|
187
|
+
OnyxSnapshotCache_1.default.clear();
|
|
185
188
|
}
|
|
186
189
|
/**
|
|
187
190
|
* Refreshes the connection manager's session ID.
|
|
188
191
|
*/
|
|
189
192
|
refreshSessionID() {
|
|
190
193
|
this.sessionID = Str.guid();
|
|
194
|
+
// Clear snapshot cache when session refreshes to avoid stale cache issues
|
|
195
|
+
OnyxSnapshotCache_1.default.clear();
|
|
191
196
|
}
|
|
192
197
|
/**
|
|
193
198
|
* Adds the connection to the eviction block list. Connections added to this list can never be evicted.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { OnyxKey, OnyxValue } from './types';
|
|
2
|
+
import type { UseOnyxOptions, UseOnyxResult, UseOnyxSelector } from './useOnyx';
|
|
3
|
+
/**
|
|
4
|
+
* Manages snapshot caching for useOnyx hook performance optimization.
|
|
5
|
+
* Handles selector function tracking and memoized getSnapshot results.
|
|
6
|
+
*/
|
|
7
|
+
declare class OnyxSnapshotCache {
|
|
8
|
+
/**
|
|
9
|
+
* Snapshot cache is a two-level map. The top-level keys are Onyx keys. The top-level values maps.
|
|
10
|
+
* The second-level keys are a custom composite string defined by this.registerConsumer. These represent a unique useOnyx config, which is not fully represented by the Onyx key alone.
|
|
11
|
+
* The reason we have two levels is for performance: not to make cache access faster, but to make cache invalidation faster.
|
|
12
|
+
* We can invalidate the snapshot cache for a given Onyx key with one map.delete operation on the top-level map, rather than having to loop through a large single-level map and delete any matching keys.
|
|
13
|
+
*/
|
|
14
|
+
private snapshotCache;
|
|
15
|
+
/**
|
|
16
|
+
* Maps selector functions to unique IDs for cache key generation
|
|
17
|
+
*/
|
|
18
|
+
private selectorIDMap;
|
|
19
|
+
/**
|
|
20
|
+
* Counter for generating incremental selector IDs
|
|
21
|
+
*/
|
|
22
|
+
private selectorIDCounter;
|
|
23
|
+
/**
|
|
24
|
+
* Reference counting for cache keys to enable automatic cleanup.
|
|
25
|
+
* Maps cache key (string) to number of consumers using it.
|
|
26
|
+
*/
|
|
27
|
+
private cacheKeyRefCounts;
|
|
28
|
+
constructor();
|
|
29
|
+
/**
|
|
30
|
+
* Generate unique ID for selector functions using incrementing numbers
|
|
31
|
+
*/
|
|
32
|
+
getSelectorID<TKey extends OnyxKey, TReturnValue>(selector: UseOnyxSelector<TKey, TReturnValue>): number;
|
|
33
|
+
/**
|
|
34
|
+
* Register a consumer for a cache key and return the cache key.
|
|
35
|
+
* Generates cache key and increments reference counter.
|
|
36
|
+
*
|
|
37
|
+
* The properties used to generate the cache key are handpicked for performance reasons and
|
|
38
|
+
* according to their purpose and effect they produce in the useOnyx hook behavior:
|
|
39
|
+
*
|
|
40
|
+
* - `selector`: Different selectors produce different results, so each selector needs its own cache entry
|
|
41
|
+
* - `initWithStoredValues`: This flag changes the initial loading behavior and affects the returned fetch status
|
|
42
|
+
* - `allowStaleData`: Controls whether stale data can be returned during pending merges, affecting result timing
|
|
43
|
+
* - `canBeMissing`: Determines logging behavior for missing data, but doesn't affect the actual data returned
|
|
44
|
+
*
|
|
45
|
+
* Other options like `canEvict`, `reuseConnection`, and `allowDynamicKey` don't affect the data transformation
|
|
46
|
+
* or timing behavior of getSnapshot, so they're excluded from the cache key for better cache hit rates.
|
|
47
|
+
*/
|
|
48
|
+
registerConsumer<TKey extends OnyxKey, TReturnValue>(options: Pick<UseOnyxOptions<TKey, TReturnValue>, 'selector' | 'initWithStoredValues' | 'allowStaleData' | 'canBeMissing'>): string;
|
|
49
|
+
/**
|
|
50
|
+
* Deregister a consumer for a cache key.
|
|
51
|
+
* Decrements reference counter and removes cache entry if no consumers remain.
|
|
52
|
+
*/
|
|
53
|
+
deregisterConsumer(key: OnyxKey, cacheKey: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* Get cached snapshot result for a key and cache key combination
|
|
56
|
+
*/
|
|
57
|
+
getCachedResult<TResult extends UseOnyxResult<OnyxValue<OnyxKey>>>(key: OnyxKey, cacheKey: string): TResult | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Set cached snapshot result for a key and cache key combination
|
|
60
|
+
*/
|
|
61
|
+
setCachedResult<TResult extends UseOnyxResult<OnyxValue<OnyxKey>>>(key: OnyxKey, cacheKey: string, result: TResult): void;
|
|
62
|
+
/**
|
|
63
|
+
* Selective cache invalidation to prevent data unavailability
|
|
64
|
+
* Collection members invalidate upward, collections don't cascade downward
|
|
65
|
+
*/
|
|
66
|
+
invalidateForKey(keyToInvalidate: OnyxKey): void;
|
|
67
|
+
/**
|
|
68
|
+
* Clear all snapshot cache
|
|
69
|
+
*/
|
|
70
|
+
clear(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Clear selector ID mappings (useful for testing)
|
|
73
|
+
*/
|
|
74
|
+
clearSelectorIds(): void;
|
|
75
|
+
}
|
|
76
|
+
declare const onyxSnapshotCache: OnyxSnapshotCache;
|
|
77
|
+
export default onyxSnapshotCache;
|
|
78
|
+
export { OnyxSnapshotCache };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OnyxSnapshotCache = void 0;
|
|
7
|
+
const OnyxUtils_1 = __importDefault(require("./OnyxUtils"));
|
|
8
|
+
/**
|
|
9
|
+
* Manages snapshot caching for useOnyx hook performance optimization.
|
|
10
|
+
* Handles selector function tracking and memoized getSnapshot results.
|
|
11
|
+
*/
|
|
12
|
+
class OnyxSnapshotCache {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.snapshotCache = new Map();
|
|
15
|
+
this.selectorIDMap = new Map();
|
|
16
|
+
this.selectorIDCounter = 0;
|
|
17
|
+
this.cacheKeyRefCounts = new Map();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Generate unique ID for selector functions using incrementing numbers
|
|
21
|
+
*/
|
|
22
|
+
getSelectorID(selector) {
|
|
23
|
+
const typedSelector = selector;
|
|
24
|
+
if (!this.selectorIDMap.has(typedSelector)) {
|
|
25
|
+
const id = this.selectorIDCounter++;
|
|
26
|
+
this.selectorIDMap.set(typedSelector, id);
|
|
27
|
+
}
|
|
28
|
+
return this.selectorIDMap.get(typedSelector);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Register a consumer for a cache key and return the cache key.
|
|
32
|
+
* Generates cache key and increments reference counter.
|
|
33
|
+
*
|
|
34
|
+
* The properties used to generate the cache key are handpicked for performance reasons and
|
|
35
|
+
* according to their purpose and effect they produce in the useOnyx hook behavior:
|
|
36
|
+
*
|
|
37
|
+
* - `selector`: Different selectors produce different results, so each selector needs its own cache entry
|
|
38
|
+
* - `initWithStoredValues`: This flag changes the initial loading behavior and affects the returned fetch status
|
|
39
|
+
* - `allowStaleData`: Controls whether stale data can be returned during pending merges, affecting result timing
|
|
40
|
+
* - `canBeMissing`: Determines logging behavior for missing data, but doesn't affect the actual data returned
|
|
41
|
+
*
|
|
42
|
+
* Other options like `canEvict`, `reuseConnection`, and `allowDynamicKey` don't affect the data transformation
|
|
43
|
+
* or timing behavior of getSnapshot, so they're excluded from the cache key for better cache hit rates.
|
|
44
|
+
*/
|
|
45
|
+
registerConsumer(options) {
|
|
46
|
+
var _a, _b, _c;
|
|
47
|
+
const selectorID = (options === null || options === void 0 ? void 0 : options.selector) ? this.getSelectorID(options.selector) : 'no_selector';
|
|
48
|
+
// Create options hash without expensive JSON.stringify
|
|
49
|
+
const initWithStoredValues = (_a = options === null || options === void 0 ? void 0 : options.initWithStoredValues) !== null && _a !== void 0 ? _a : true;
|
|
50
|
+
const allowStaleData = (_b = options === null || options === void 0 ? void 0 : options.allowStaleData) !== null && _b !== void 0 ? _b : false;
|
|
51
|
+
const canBeMissing = (_c = options === null || options === void 0 ? void 0 : options.canBeMissing) !== null && _c !== void 0 ? _c : true;
|
|
52
|
+
const cacheKey = `${selectorID}_${initWithStoredValues}_${allowStaleData}_${canBeMissing}`;
|
|
53
|
+
// Increment reference count for this cache key
|
|
54
|
+
const currentCount = this.cacheKeyRefCounts.get(cacheKey) || 0;
|
|
55
|
+
this.cacheKeyRefCounts.set(cacheKey, currentCount + 1);
|
|
56
|
+
return cacheKey;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Deregister a consumer for a cache key.
|
|
60
|
+
* Decrements reference counter and removes cache entry if no consumers remain.
|
|
61
|
+
*/
|
|
62
|
+
deregisterConsumer(key, cacheKey) {
|
|
63
|
+
const currentCount = this.cacheKeyRefCounts.get(cacheKey) || 0;
|
|
64
|
+
if (currentCount <= 1) {
|
|
65
|
+
// Last consumer - remove from reference counter and cache
|
|
66
|
+
this.cacheKeyRefCounts.delete(cacheKey);
|
|
67
|
+
// Remove from snapshot cache
|
|
68
|
+
const keyCache = this.snapshotCache.get(key);
|
|
69
|
+
if (keyCache) {
|
|
70
|
+
keyCache.delete(cacheKey);
|
|
71
|
+
// If this was the last cache entry for this Onyx key, remove the key entirely
|
|
72
|
+
if (keyCache.size === 0) {
|
|
73
|
+
this.snapshotCache.delete(key);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// Still has other consumers - just decrement count
|
|
79
|
+
this.cacheKeyRefCounts.set(cacheKey, currentCount - 1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get cached snapshot result for a key and cache key combination
|
|
84
|
+
*/
|
|
85
|
+
getCachedResult(key, cacheKey) {
|
|
86
|
+
const keyCache = this.snapshotCache.get(key);
|
|
87
|
+
return keyCache === null || keyCache === void 0 ? void 0 : keyCache.get(cacheKey);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Set cached snapshot result for a key and cache key combination
|
|
91
|
+
*/
|
|
92
|
+
setCachedResult(key, cacheKey, result) {
|
|
93
|
+
if (!this.snapshotCache.has(key)) {
|
|
94
|
+
this.snapshotCache.set(key, new Map());
|
|
95
|
+
}
|
|
96
|
+
this.snapshotCache.get(key).set(cacheKey, result);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Selective cache invalidation to prevent data unavailability
|
|
100
|
+
* Collection members invalidate upward, collections don't cascade downward
|
|
101
|
+
*/
|
|
102
|
+
invalidateForKey(keyToInvalidate) {
|
|
103
|
+
// Always invalidate the exact key
|
|
104
|
+
this.snapshotCache.delete(keyToInvalidate);
|
|
105
|
+
try {
|
|
106
|
+
// Check if the key is a collection member and invalidate the collection base key
|
|
107
|
+
const collectionBaseKey = OnyxUtils_1.default.getCollectionKey(keyToInvalidate);
|
|
108
|
+
this.snapshotCache.delete(collectionBaseKey);
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
// do nothing - this just means the key is not a collection member
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Clear all snapshot cache
|
|
116
|
+
*/
|
|
117
|
+
clear() {
|
|
118
|
+
this.snapshotCache.clear();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Clear selector ID mappings (useful for testing)
|
|
122
|
+
*/
|
|
123
|
+
clearSelectorIds() {
|
|
124
|
+
this.selectorIDCounter = 0;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.OnyxSnapshotCache = OnyxSnapshotCache;
|
|
128
|
+
// Create and export a singleton instance
|
|
129
|
+
const onyxSnapshotCache = new OnyxSnapshotCache();
|
|
130
|
+
exports.default = onyxSnapshotCache;
|
package/dist/OnyxUtils.d.ts
CHANGED
|
@@ -143,14 +143,14 @@ declare function getCachedCollection<TKey extends CollectionKeyBase>(collectionK
|
|
|
143
143
|
/**
|
|
144
144
|
* When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks
|
|
145
145
|
*/
|
|
146
|
-
declare function keysChanged<TKey extends CollectionKeyBase>(collectionKey: TKey, partialCollection: OnyxCollection<KeyValueMapping[TKey]>, partialPreviousCollection: OnyxCollection<KeyValueMapping[TKey]> | undefined, notifyConnectSubscribers?: boolean, notifyWithOnyxSubscribers?: boolean): void;
|
|
146
|
+
declare function keysChanged<TKey extends CollectionKeyBase>(collectionKey: TKey, partialCollection: OnyxCollection<KeyValueMapping[TKey]>, partialPreviousCollection: OnyxCollection<KeyValueMapping[TKey]> | undefined, notifyConnectSubscribers?: boolean, notifyWithOnyxSubscribers?: boolean, notifyUseOnyxHookSubscribers?: boolean): void;
|
|
147
147
|
/**
|
|
148
148
|
* When a key change happens, search for any callbacks matching the key or collection key and trigger those callbacks
|
|
149
149
|
*
|
|
150
150
|
* @example
|
|
151
151
|
* keyChanged(key, value, subscriber => subscriber.initWithStoredValues === false)
|
|
152
152
|
*/
|
|
153
|
-
declare function keyChanged<TKey extends OnyxKey>(key: TKey, value: OnyxValue<TKey>, previousValue: OnyxValue<TKey>, canUpdateSubscriber?: (subscriber?: Mapping<OnyxKey>) => boolean, notifyConnectSubscribers?: boolean, notifyWithOnyxSubscribers?: boolean): void;
|
|
153
|
+
declare function keyChanged<TKey extends OnyxKey>(key: TKey, value: OnyxValue<TKey>, previousValue: OnyxValue<TKey>, canUpdateSubscriber?: (subscriber?: Mapping<OnyxKey>) => boolean, notifyConnectSubscribers?: boolean, notifyWithOnyxSubscribers?: boolean, notifyUseOnyxHookSubscribers?: boolean): void;
|
|
154
154
|
/**
|
|
155
155
|
* Sends the data obtained from the keys to the connection. It either:
|
|
156
156
|
* - sets state on the withOnyxInstances
|
package/dist/OnyxUtils.js
CHANGED
|
@@ -512,7 +512,7 @@ function getCachedCollection(collectionKey, collectionMemberKeys) {
|
|
|
512
512
|
/**
|
|
513
513
|
* When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks
|
|
514
514
|
*/
|
|
515
|
-
function keysChanged(collectionKey, partialCollection, partialPreviousCollection, notifyConnectSubscribers = true, notifyWithOnyxSubscribers = true) {
|
|
515
|
+
function keysChanged(collectionKey, partialCollection, partialPreviousCollection, notifyConnectSubscribers = true, notifyWithOnyxSubscribers = true, notifyUseOnyxHookSubscribers = true) {
|
|
516
516
|
// We prepare the "cached collection" which is the entire collection + the new partial data that
|
|
517
517
|
// was merged in via mergeCollection().
|
|
518
518
|
const cachedCollection = getCachedCollection(collectionKey);
|
|
@@ -540,7 +540,8 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
|
|
|
540
540
|
const isSubscribedToCollectionMemberKey = isCollectionMemberKey(collectionKey, subscriber.key);
|
|
541
541
|
// Regular Onyx.connect() subscriber found.
|
|
542
542
|
if (typeof subscriber.callback === 'function') {
|
|
543
|
-
if (
|
|
543
|
+
// Check if it's a useOnyx or a regular Onyx.connect() subscriber
|
|
544
|
+
if ((subscriber.isUseOnyxSubscriber && !notifyUseOnyxHookSubscribers) || (!subscriber.isUseOnyxSubscriber && !notifyConnectSubscribers)) {
|
|
544
545
|
continue;
|
|
545
546
|
}
|
|
546
547
|
// If they are subscribed to the collection key and using waitForCollectionCallback then we'll
|
|
@@ -670,7 +671,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
|
|
|
670
671
|
* @example
|
|
671
672
|
* keyChanged(key, value, subscriber => subscriber.initWithStoredValues === false)
|
|
672
673
|
*/
|
|
673
|
-
function keyChanged(key, value, previousValue, canUpdateSubscriber = () => true, notifyConnectSubscribers = true, notifyWithOnyxSubscribers = true) {
|
|
674
|
+
function keyChanged(key, value, previousValue, canUpdateSubscriber = () => true, notifyConnectSubscribers = true, notifyWithOnyxSubscribers = true, notifyUseOnyxHookSubscribers = true) {
|
|
674
675
|
var _a, _b;
|
|
675
676
|
// Add or remove this key from the recentlyAccessedKeys lists
|
|
676
677
|
if (value !== null) {
|
|
@@ -709,7 +710,8 @@ function keyChanged(key, value, previousValue, canUpdateSubscriber = () => true,
|
|
|
709
710
|
}
|
|
710
711
|
// Subscriber is a regular call to connect() and provided a callback
|
|
711
712
|
if (typeof subscriber.callback === 'function') {
|
|
712
|
-
if (
|
|
713
|
+
// Check if it's a useOnyx or a regular Onyx.connect() subscriber
|
|
714
|
+
if ((subscriber.isUseOnyxSubscriber && !notifyUseOnyxHookSubscribers) || (!subscriber.isUseOnyxSubscriber && !notifyConnectSubscribers)) {
|
|
713
715
|
continue;
|
|
714
716
|
}
|
|
715
717
|
if (lastConnectionCallbackData.has(subscriber.subscriptionID) && lastConnectionCallbackData.get(subscriber.subscriptionID) === value) {
|
|
@@ -889,8 +891,8 @@ function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
|
|
|
889
891
|
* scheduleSubscriberUpdate(key, value, subscriber => subscriber.initWithStoredValues === false)
|
|
890
892
|
*/
|
|
891
893
|
function scheduleSubscriberUpdate(key, value, previousValue, canUpdateSubscriber = () => true) {
|
|
892
|
-
const promise = Promise.resolve().then(() => keyChanged(key, value, previousValue, canUpdateSubscriber, true, false));
|
|
893
|
-
batchUpdates(() => keyChanged(key, value, previousValue, canUpdateSubscriber, false, true));
|
|
894
|
+
const promise = Promise.resolve().then(() => keyChanged(key, value, previousValue, canUpdateSubscriber, true, false, false));
|
|
895
|
+
batchUpdates(() => keyChanged(key, value, previousValue, canUpdateSubscriber, false, true, true));
|
|
894
896
|
return Promise.all([maybeFlushBatchUpdates(), promise]).then(() => undefined);
|
|
895
897
|
}
|
|
896
898
|
/**
|
|
@@ -899,8 +901,8 @@ function scheduleSubscriberUpdate(key, value, previousValue, canUpdateSubscriber
|
|
|
899
901
|
* subscriber callbacks receive the data in a different format than they normally expect and it breaks code.
|
|
900
902
|
*/
|
|
901
903
|
function scheduleNotifyCollectionSubscribers(key, value, previousValue) {
|
|
902
|
-
const promise = Promise.resolve().then(() => keysChanged(key, value, previousValue, true, false));
|
|
903
|
-
batchUpdates(() => keysChanged(key, value, previousValue, false, true));
|
|
904
|
+
const promise = Promise.resolve().then(() => keysChanged(key, value, previousValue, true, false, false));
|
|
905
|
+
batchUpdates(() => keysChanged(key, value, previousValue, false, true, true));
|
|
904
906
|
return Promise.all([maybeFlushBatchUpdates(), promise]).then(() => undefined);
|
|
905
907
|
}
|
|
906
908
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -235,6 +235,8 @@ type BaseConnectOptions = {
|
|
|
235
235
|
* with the same connect configurations.
|
|
236
236
|
*/
|
|
237
237
|
reuseConnection?: boolean;
|
|
238
|
+
/** Indicates whether this subscriber is created from the useOnyx hook. */
|
|
239
|
+
isUseOnyxSubscriber?: boolean;
|
|
238
240
|
};
|
|
239
241
|
/** Represents the callback function used in `Onyx.connect()` method with a regular key. */
|
|
240
242
|
type DefaultConnectCallback<TKey extends OnyxKey> = (value: OnyxEntry<KeyValueMapping[TKey]>, key: TKey) => void;
|
package/dist/useOnyx.d.ts
CHANGED
|
@@ -47,4 +47,4 @@ type ResultMetadata<TValue> = {
|
|
|
47
47
|
type UseOnyxResult<TValue> = [NonNullable<TValue> | undefined, ResultMetadata<TValue>];
|
|
48
48
|
declare function useOnyx<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>>(key: TKey, options?: UseOnyxOptions<TKey, TReturnValue>, dependencies?: DependencyList): UseOnyxResult<TReturnValue>;
|
|
49
49
|
export default useOnyx;
|
|
50
|
-
export type { FetchStatus, ResultMetadata, UseOnyxResult, UseOnyxOptions };
|
|
50
|
+
export type { FetchStatus, ResultMetadata, UseOnyxResult, UseOnyxOptions, UseOnyxSelector };
|
package/dist/useOnyx.js
CHANGED
|
@@ -45,6 +45,7 @@ const GlobalSettings = __importStar(require("./GlobalSettings"));
|
|
|
45
45
|
const usePrevious_1 = __importDefault(require("./usePrevious"));
|
|
46
46
|
const metrics_1 = __importDefault(require("./metrics"));
|
|
47
47
|
const Logger = __importStar(require("./Logger"));
|
|
48
|
+
const OnyxSnapshotCache_1 = __importDefault(require("./OnyxSnapshotCache"));
|
|
48
49
|
const useLiveRef_1 = __importDefault(require("./useLiveRef"));
|
|
49
50
|
function useOnyx(key, options, dependencies = []) {
|
|
50
51
|
const connectionRef = (0, react_1.useRef)(null);
|
|
@@ -105,6 +106,14 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
105
106
|
const shouldGetCachedValueRef = (0, react_1.useRef)(true);
|
|
106
107
|
// Inside useOnyx.ts, we need to track the sourceValue separately
|
|
107
108
|
const sourceValueRef = (0, react_1.useRef)(undefined);
|
|
109
|
+
// Cache the options key to avoid regenerating it every getSnapshot call
|
|
110
|
+
const cacheKey = (0, react_1.useMemo)(() => OnyxSnapshotCache_1.default.registerConsumer({
|
|
111
|
+
selector: options === null || options === void 0 ? void 0 : options.selector,
|
|
112
|
+
initWithStoredValues: options === null || options === void 0 ? void 0 : options.initWithStoredValues,
|
|
113
|
+
allowStaleData: options === null || options === void 0 ? void 0 : options.allowStaleData,
|
|
114
|
+
canBeMissing: options === null || options === void 0 ? void 0 : options.canBeMissing,
|
|
115
|
+
}), [options === null || options === void 0 ? void 0 : options.selector, options === null || options === void 0 ? void 0 : options.initWithStoredValues, options === null || options === void 0 ? void 0 : options.allowStaleData, options === null || options === void 0 ? void 0 : options.canBeMissing]);
|
|
116
|
+
(0, react_1.useEffect)(() => () => OnyxSnapshotCache_1.default.deregisterConsumer(key, cacheKey), [key, cacheKey]);
|
|
108
117
|
(0, react_1.useEffect)(() => {
|
|
109
118
|
// These conditions will ensure we can only handle dynamic collection member keys from the same collection.
|
|
110
119
|
if ((options === null || options === void 0 ? void 0 : options.allowDynamicKey) || previousKey === key) {
|
|
@@ -137,6 +146,8 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
137
146
|
if (connectionRef.current === null || isConnectingRef.current || !onStoreChangeFnRef.current) {
|
|
138
147
|
return;
|
|
139
148
|
}
|
|
149
|
+
// Invalidate cache when dependencies change so selector runs with new closure values
|
|
150
|
+
OnyxSnapshotCache_1.default.invalidateForKey(key);
|
|
140
151
|
shouldGetCachedValueRef.current = true;
|
|
141
152
|
onStoreChangeFnRef.current();
|
|
142
153
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -158,10 +169,23 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
158
169
|
}, [key, options === null || options === void 0 ? void 0 : options.canEvict]);
|
|
159
170
|
const getSnapshot = (0, react_1.useCallback)(() => {
|
|
160
171
|
var _a, _b, _c, _d;
|
|
172
|
+
// Check if we have any cache for this Onyx key
|
|
173
|
+
// Don't use cache for first connection with initWithStoredValues: false
|
|
174
|
+
// Also don't use cache during active data updates (when shouldGetCachedValueRef is true)
|
|
175
|
+
if (!(isFirstConnectionRef.current && (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false) && !shouldGetCachedValueRef.current) {
|
|
176
|
+
const cachedResult = OnyxSnapshotCache_1.default.getCachedResult(key, cacheKey);
|
|
177
|
+
if (cachedResult !== undefined) {
|
|
178
|
+
resultRef.current = cachedResult;
|
|
179
|
+
return cachedResult;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
161
182
|
let isOnyxValueDefined = true;
|
|
162
183
|
// We return the initial result right away during the first connection if `initWithStoredValues` is set to `false`.
|
|
163
184
|
if (isFirstConnectionRef.current && (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false) {
|
|
164
|
-
|
|
185
|
+
const result = resultRef.current;
|
|
186
|
+
// Store result in snapshot cache
|
|
187
|
+
OnyxSnapshotCache_1.default.setCachedResult(key, cacheKey, result);
|
|
188
|
+
return result;
|
|
165
189
|
}
|
|
166
190
|
// We get the value from cache while the first connection to Onyx is being made,
|
|
167
191
|
// so we can return any cached value right away. After the connection is made, we only
|
|
@@ -188,7 +212,7 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
188
212
|
newValueRef.current = undefined;
|
|
189
213
|
newFetchStatus = 'loading';
|
|
190
214
|
}
|
|
191
|
-
// Optimized equality checking
|
|
215
|
+
// Optimized equality checking:
|
|
192
216
|
// - Memoized selectors already handle deep equality internally, so we can use fast reference equality
|
|
193
217
|
// - Non-selector cases use shallow equality for object reference checks
|
|
194
218
|
// - Normalize null to undefined to ensure consistent comparison (both represent "no value")
|
|
@@ -226,8 +250,9 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
226
250
|
Logger.logAlert(`useOnyx returned no data for key with canBeMissing set to false for key ${key}`, { showAlert: true });
|
|
227
251
|
}
|
|
228
252
|
}
|
|
253
|
+
OnyxSnapshotCache_1.default.setCachedResult(key, cacheKey, resultRef.current);
|
|
229
254
|
return resultRef.current;
|
|
230
|
-
}, [options === null || options === void 0 ? void 0 : options.initWithStoredValues, options === null || options === void 0 ? void 0 : options.allowStaleData, options === null || options === void 0 ? void 0 : options.canBeMissing, key, memoizedSelector]);
|
|
255
|
+
}, [options === null || options === void 0 ? void 0 : options.initWithStoredValues, options === null || options === void 0 ? void 0 : options.allowStaleData, options === null || options === void 0 ? void 0 : options.canBeMissing, key, memoizedSelector, cacheKey]);
|
|
231
256
|
const subscribe = (0, react_1.useCallback)((onStoreChange) => {
|
|
232
257
|
isConnectingRef.current = true;
|
|
233
258
|
onStoreChangeFnRef.current = onStoreChange;
|
|
@@ -243,12 +268,15 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
243
268
|
shouldGetCachedValueRef.current = true;
|
|
244
269
|
// sourceValue is unknown type, so we need to cast it to the correct type.
|
|
245
270
|
sourceValueRef.current = sourceValue;
|
|
271
|
+
// Invalidate snapshot cache for this key when data changes
|
|
272
|
+
OnyxSnapshotCache_1.default.invalidateForKey(key);
|
|
246
273
|
// Finally, we signal that the store changed, making `getSnapshot()` be called again.
|
|
247
274
|
onStoreChange();
|
|
248
275
|
},
|
|
249
276
|
initWithStoredValues: options === null || options === void 0 ? void 0 : options.initWithStoredValues,
|
|
250
277
|
waitForCollectionCallback: OnyxUtils_1.default.isCollectionKey(key),
|
|
251
278
|
reuseConnection: options === null || options === void 0 ? void 0 : options.reuseConnection,
|
|
279
|
+
isUseOnyxSubscriber: true,
|
|
252
280
|
});
|
|
253
281
|
checkEvictableKey();
|
|
254
282
|
return () => {
|