react-native-onyx 3.0.69 → 3.0.71
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/OnyxUtils.js +55 -58
- package/dist/useOnyx.js +31 -29
- package/package.json +1 -1
package/dist/OnyxUtils.js
CHANGED
|
@@ -425,8 +425,8 @@ function getCachedCollection(collectionKey, collectionMemberKeys) {
|
|
|
425
425
|
}
|
|
426
426
|
return filteredCollection;
|
|
427
427
|
}
|
|
428
|
-
//
|
|
429
|
-
return
|
|
428
|
+
// Snapshot is frozen — safe to return by reference
|
|
429
|
+
return collectionData;
|
|
430
430
|
}
|
|
431
431
|
// Fallback to original implementation if collection data not available
|
|
432
432
|
const collection = {};
|
|
@@ -450,70 +450,63 @@ function getCachedCollection(collectionKey, collectionMemberKeys) {
|
|
|
450
450
|
* When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks
|
|
451
451
|
*/
|
|
452
452
|
function keysChanged(collectionKey, partialCollection, partialPreviousCollection) {
|
|
453
|
-
|
|
454
|
-
// was merged in via mergeCollection().
|
|
453
|
+
var _a;
|
|
455
454
|
const cachedCollection = getCachedCollection(collectionKey);
|
|
456
455
|
const previousCollection = partialPreviousCollection !== null && partialPreviousCollection !== void 0 ? partialPreviousCollection : {};
|
|
457
|
-
|
|
458
|
-
//
|
|
459
|
-
//
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
456
|
+
const changedMemberKeys = Object.keys(partialCollection !== null && partialCollection !== void 0 ? partialCollection : {});
|
|
457
|
+
// Use indexed lookup instead of scanning all subscribers.
|
|
458
|
+
// We need subscribers for: (1) the collection key itself, and (2) individual changed member keys.
|
|
459
|
+
const collectionSubscriberIDs = (_a = onyxKeyToSubscriptionIDs.get(collectionKey)) !== null && _a !== void 0 ? _a : [];
|
|
460
|
+
const memberSubscriberIDs = [];
|
|
461
|
+
for (const memberKey of changedMemberKeys) {
|
|
462
|
+
const ids = onyxKeyToSubscriptionIDs.get(memberKey);
|
|
463
|
+
if (ids) {
|
|
464
|
+
for (const id of ids) {
|
|
465
|
+
memberSubscriberIDs.push(id);
|
|
466
|
+
}
|
|
465
467
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
+
}
|
|
469
|
+
// Notify collection-level subscribers
|
|
470
|
+
for (const subID of collectionSubscriberIDs) {
|
|
471
|
+
const subscriber = callbackToStateMapping[subID];
|
|
472
|
+
if (!subscriber || typeof subscriber.callback !== 'function') {
|
|
468
473
|
continue;
|
|
469
474
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* e.g. Onyx.connect({key: `${ONYXKEYS.COLLECTION.REPORT}{reportID}`, callback: ...});
|
|
476
|
-
*/
|
|
477
|
-
const isSubscribedToCollectionMemberKey = OnyxKeys_1.default.isCollectionMemberKey(collectionKey, subscriber.key);
|
|
478
|
-
// Regular Onyx.connect() subscriber found.
|
|
479
|
-
if (typeof subscriber.callback === 'function') {
|
|
480
|
-
try {
|
|
481
|
-
// If they are subscribed to the collection key and using waitForCollectionCallback then we'll
|
|
482
|
-
// send the whole cached collection.
|
|
483
|
-
if (isSubscribedToCollectionKey) {
|
|
484
|
-
lastConnectionCallbackData.set(subscriber.subscriptionID, { value: cachedCollection, matchedKey: subscriber.key });
|
|
485
|
-
if (subscriber.waitForCollectionCallback) {
|
|
486
|
-
subscriber.callback(cachedCollection, subscriber.key, partialCollection);
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
// If they are not using waitForCollectionCallback then we notify the subscriber with
|
|
490
|
-
// the new merged data but only for any keys in the partial collection.
|
|
491
|
-
const dataKeys = Object.keys(partialCollection !== null && partialCollection !== void 0 ? partialCollection : {});
|
|
492
|
-
for (const dataKey of dataKeys) {
|
|
493
|
-
if ((0, fast_equals_1.deepEqual)(cachedCollection[dataKey], previousCollection[dataKey])) {
|
|
494
|
-
continue;
|
|
495
|
-
}
|
|
496
|
-
subscriber.callback(cachedCollection[dataKey], dataKey);
|
|
497
|
-
}
|
|
498
|
-
continue;
|
|
499
|
-
}
|
|
500
|
-
// And if the subscriber is specifically only tracking a particular collection member key then we will
|
|
501
|
-
// notify them with the cached data for that key only.
|
|
502
|
-
if (isSubscribedToCollectionMemberKey) {
|
|
503
|
-
if ((0, fast_equals_1.deepEqual)(cachedCollection[subscriber.key], previousCollection[subscriber.key])) {
|
|
504
|
-
continue;
|
|
505
|
-
}
|
|
506
|
-
const subscriberCallback = subscriber.callback;
|
|
507
|
-
subscriberCallback(cachedCollection[subscriber.key], subscriber.key);
|
|
508
|
-
lastConnectionCallbackData.set(subscriber.subscriptionID, { value: cachedCollection[subscriber.key], matchedKey: subscriber.key });
|
|
509
|
-
continue;
|
|
510
|
-
}
|
|
475
|
+
try {
|
|
476
|
+
lastConnectionCallbackData.set(subscriber.subscriptionID, { value: cachedCollection, matchedKey: subscriber.key });
|
|
477
|
+
if (subscriber.waitForCollectionCallback) {
|
|
478
|
+
subscriber.callback(cachedCollection, subscriber.key, partialCollection);
|
|
511
479
|
continue;
|
|
512
480
|
}
|
|
513
|
-
|
|
514
|
-
|
|
481
|
+
// Not using waitForCollectionCallback — notify per changed key
|
|
482
|
+
for (const dataKey of changedMemberKeys) {
|
|
483
|
+
if (cachedCollection[dataKey] === previousCollection[dataKey]) {
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
subscriber.callback(cachedCollection[dataKey], dataKey);
|
|
515
487
|
}
|
|
516
488
|
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
Logger.logAlert(`[OnyxUtils.keysChanged] Subscriber callback threw an error for key '${collectionKey}': ${error}`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
// Notify member-level subscribers (e.g. subscribed to `report_123`)
|
|
494
|
+
for (const subID of memberSubscriberIDs) {
|
|
495
|
+
const subscriber = callbackToStateMapping[subID];
|
|
496
|
+
if (!subscriber || typeof subscriber.callback !== 'function') {
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
if (cachedCollection[subscriber.key] === previousCollection[subscriber.key]) {
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
try {
|
|
503
|
+
const subscriberCallback = subscriber.callback;
|
|
504
|
+
subscriberCallback(cachedCollection[subscriber.key], subscriber.key);
|
|
505
|
+
lastConnectionCallbackData.set(subscriber.subscriptionID, { value: cachedCollection[subscriber.key], matchedKey: subscriber.key });
|
|
506
|
+
}
|
|
507
|
+
catch (error) {
|
|
508
|
+
Logger.logAlert(`[OnyxUtils.keysChanged] Subscriber callback threw an error for key '${collectionKey}': ${error}`);
|
|
509
|
+
}
|
|
517
510
|
}
|
|
518
511
|
}
|
|
519
512
|
/**
|
|
@@ -545,6 +538,9 @@ function keyChanged(key, value, canUpdateSubscriber = () => true, isProcessingCo
|
|
|
545
538
|
return;
|
|
546
539
|
}
|
|
547
540
|
}
|
|
541
|
+
// Cache the collection snapshot per dispatch so all subscribers to the same collection
|
|
542
|
+
// see a consistent view, even if an earlier subscriber's callback synchronously writes
|
|
543
|
+
// to the same collection.
|
|
548
544
|
const cachedCollections = {};
|
|
549
545
|
for (const stateMappingKey of stateMappingKeys) {
|
|
550
546
|
const subscriber = callbackToStateMapping[stateMappingKey];
|
|
@@ -564,12 +560,13 @@ function keyChanged(key, value, canUpdateSubscriber = () => true, isProcessingCo
|
|
|
564
560
|
if (isProcessingCollectionUpdate) {
|
|
565
561
|
continue;
|
|
566
562
|
}
|
|
563
|
+
// Cache once per dispatch to ensure all subscribers see a consistent snapshot
|
|
564
|
+
// even if a previous callback synchronously wrote to the same collection.
|
|
567
565
|
let cachedCollection = cachedCollections[subscriber.key];
|
|
568
566
|
if (!cachedCollection) {
|
|
569
567
|
cachedCollection = getCachedCollection(subscriber.key);
|
|
570
568
|
cachedCollections[subscriber.key] = cachedCollection;
|
|
571
569
|
}
|
|
572
|
-
cachedCollection[key] = value;
|
|
573
570
|
lastConnectionCallbackData.set(subscriber.subscriptionID, { value: cachedCollection, matchedKey: subscriber.key });
|
|
574
571
|
subscriber.callback(cachedCollection, subscriber.key, { [key]: value });
|
|
575
572
|
continue;
|
package/dist/useOnyx.js
CHANGED
|
@@ -62,16 +62,15 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
62
62
|
// Recompute if input changed, dependencies changed, or first time
|
|
63
63
|
const dependenciesChanged = !(0, fast_equals_1.shallowEqual)(lastDependencies, currentDependencies);
|
|
64
64
|
if (!hasComputed || lastInput !== input || dependenciesChanged) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
65
|
+
const newOutput = selector(input);
|
|
66
|
+
// Always track the current input to avoid re-running the selector
|
|
67
|
+
// when the same input is seen again (even if the output didn't change).
|
|
68
|
+
lastInput = input;
|
|
69
|
+
// Only update the output reference if it actually changed
|
|
70
|
+
if (!hasComputed || !(0, fast_equals_1.deepEqual)(lastOutput, newOutput) || dependenciesChanged) {
|
|
71
|
+
lastOutput = newOutput;
|
|
72
|
+
lastDependencies = [...currentDependencies];
|
|
73
|
+
hasComputed = true;
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
return lastOutput;
|
|
@@ -97,6 +96,9 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
97
96
|
// after cleanup), so the hook automatically enters first-connection mode for the new key without any
|
|
98
97
|
// explicit reset logic — eliminating the race condition where cleanup could clobber a boolean flag.
|
|
99
98
|
const connectedKeyRef = (0, react_1.useRef)(null);
|
|
99
|
+
// Tracks whether the hook has completed its initial mount subscription.
|
|
100
|
+
// Unlike connectedKeyRef (which gets nulled by cleanup), this persists across re-subscriptions.
|
|
101
|
+
const hasMountedRef = (0, react_1.useRef)(false);
|
|
100
102
|
// Indicates if the hook is connecting to an Onyx key.
|
|
101
103
|
const isConnectingRef = (0, react_1.useRef)(false);
|
|
102
104
|
// Stores the `onStoreChange()` function, which can be used to trigger a `getSnapshot()` update when desired.
|
|
@@ -137,7 +139,7 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
137
139
|
// even if all other conditions (isFirstConnection, shouldGetCachedValue, key) are false.
|
|
138
140
|
const lastComputedSelectorRef = (0, react_1.useRef)(memoizedSelector);
|
|
139
141
|
const getSnapshot = (0, react_1.useCallback)(() => {
|
|
140
|
-
var _a, _b, _c
|
|
142
|
+
var _a, _b, _c;
|
|
141
143
|
// Check if we have any cache for this Onyx key
|
|
142
144
|
// Don't use cache for first connection with initWithStoredValues: false
|
|
143
145
|
// Also don't use cache during active data updates (when shouldGetCachedValueRef is true)
|
|
@@ -179,19 +181,11 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
179
181
|
newValueRef.current = undefined;
|
|
180
182
|
newFetchStatus = 'loading';
|
|
181
183
|
}
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
if (memoizedSelector) {
|
|
188
|
-
const normalizedPrevious = (_a = previousValueRef.current) !== null && _a !== void 0 ? _a : undefined;
|
|
189
|
-
const normalizedNew = (_b = newValueRef.current) !== null && _b !== void 0 ? _b : undefined;
|
|
190
|
-
areValuesEqual = normalizedPrevious === normalizedNew;
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
areValuesEqual = (0, fast_equals_1.shallowEqual)((_c = previousValueRef.current) !== null && _c !== void 0 ? _c : undefined, newValueRef.current);
|
|
194
|
-
}
|
|
184
|
+
// shallowEqual checks === first (O(1) for frozen snapshots and stable selector references),
|
|
185
|
+
// then falls back to comparing top-level properties for individual keys that may have
|
|
186
|
+
// new references with equivalent content.
|
|
187
|
+
// Normalize null to undefined to ensure consistent comparison (both represent "no value").
|
|
188
|
+
const areValuesEqual = (0, fast_equals_1.shallowEqual)((_a = previousValueRef.current) !== null && _a !== void 0 ? _a : undefined, (_b = newValueRef.current) !== null && _b !== void 0 ? _b : undefined);
|
|
195
189
|
// We update the cached value and the result in the following conditions:
|
|
196
190
|
// We will update the cached value and the result in any of the following situations:
|
|
197
191
|
// - The previously cached value is different from the new value.
|
|
@@ -205,7 +199,7 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
205
199
|
// If the new value is `null` we default it to `undefined` to ensure the consumer gets a consistent result from the hook.
|
|
206
200
|
newFetchStatus = newFetchStatus !== null && newFetchStatus !== void 0 ? newFetchStatus : 'loaded';
|
|
207
201
|
resultRef.current = [
|
|
208
|
-
(
|
|
202
|
+
(_c = previousValueRef.current) !== null && _c !== void 0 ? _c : undefined,
|
|
209
203
|
{
|
|
210
204
|
status: newFetchStatus,
|
|
211
205
|
sourceValue: sourceValueRef.current,
|
|
@@ -220,11 +214,19 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
220
214
|
const subscribe = (0, react_1.useCallback)((onStoreChange) => {
|
|
221
215
|
// Reset internal state so the hook properly transitions through loading
|
|
222
216
|
// for the new key instead of preserving stale state from the previous one.
|
|
223
|
-
|
|
224
|
-
|
|
217
|
+
// Only reset when the key has actually changed (not on initial mount).
|
|
218
|
+
if (hasMountedRef.current) {
|
|
219
|
+
previousValueRef.current = null;
|
|
220
|
+
newValueRef.current = null;
|
|
221
|
+
sourceValueRef.current = undefined;
|
|
222
|
+
resultRef.current = [undefined, { status: (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false ? 'loaded' : 'loading' }];
|
|
223
|
+
}
|
|
224
|
+
// Force a cache re-read on every (re)subscription so any side effects from
|
|
225
|
+
// subscribeToKey (e.g. addNullishStorageKey for skippable collection member ids)
|
|
226
|
+
// are reflected in the next getSnapshot. Resetting this flag does not change
|
|
227
|
+
// resultRef by itself, so it doesn't cause an extra mount render.
|
|
225
228
|
shouldGetCachedValueRef.current = true;
|
|
226
|
-
|
|
227
|
-
resultRef.current = [undefined, { status: (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false ? 'loaded' : 'loading' }];
|
|
229
|
+
hasMountedRef.current = true;
|
|
228
230
|
isConnectingRef.current = true;
|
|
229
231
|
onStoreChangeFnRef.current = onStoreChange;
|
|
230
232
|
connectionRef.current = OnyxConnectionManager_1.default.connect({
|