react-native-onyx 1.0.54 → 1.0.56
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/web.development.js +128 -34
- package/dist/web.development.js.map +1 -1
- package/dist/web.min.js +1 -1
- package/dist/web.min.js.map +1 -1
- package/lib/Onyx.js +92 -26
- package/lib/storage/__mocks__/index.js +1 -2
- package/lib/withOnyx.js +35 -7
- package/package.json +1 -1
package/dist/web.development.js
CHANGED
|
@@ -268,6 +268,46 @@ function isSafeEvictionKey(testKey) {
|
|
|
268
268
|
return underscore__WEBPACK_IMPORTED_MODULE_2___default().some(evictionAllowList, (key) => isKeyMatch(key, testKey));
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
|
|
273
|
+
* If the requested key is a collection, it will return an object with all the collection members.
|
|
274
|
+
*
|
|
275
|
+
* @param {String} key
|
|
276
|
+
* @param {Object} mapping
|
|
277
|
+
* @returns {Mixed}
|
|
278
|
+
*/
|
|
279
|
+
function tryGetCachedValue(key) {let mapping = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
280
|
+
let val = _OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].getValue(key);
|
|
281
|
+
|
|
282
|
+
if (isCollectionKey(key)) {
|
|
283
|
+
const allKeys = _OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].getAllKeys();
|
|
284
|
+
const matchingKeys = underscore__WEBPACK_IMPORTED_MODULE_2___default().filter(allKeys, (k) => k.startsWith(key));
|
|
285
|
+
const values = underscore__WEBPACK_IMPORTED_MODULE_2___default().reduce(matchingKeys, (finalObject, matchedKey) => {
|
|
286
|
+
const cachedValue = _OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].getValue(matchedKey);
|
|
287
|
+
if (cachedValue) {
|
|
288
|
+
// This is permissible because we're in the process of constructing the final object in a reduce function.
|
|
289
|
+
// eslint-disable-next-line no-param-reassign
|
|
290
|
+
finalObject[matchedKey] = cachedValue;
|
|
291
|
+
}
|
|
292
|
+
return finalObject;
|
|
293
|
+
}, {});
|
|
294
|
+
if (underscore__WEBPACK_IMPORTED_MODULE_2___default().isEmpty(values)) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
val = values;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (mapping.selector) {
|
|
301
|
+
const state = mapping.withOnyxInstance ? mapping.withOnyxInstance.state : undefined;
|
|
302
|
+
if (isCollectionKey(key)) {
|
|
303
|
+
return reduceCollectionWithSelector(val, mapping.selector, state);
|
|
304
|
+
}
|
|
305
|
+
return getSubsetOfData(val, mapping.selector, state);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return val;
|
|
309
|
+
}
|
|
310
|
+
|
|
271
311
|
/**
|
|
272
312
|
* Remove a key from the recently accessed key list.
|
|
273
313
|
*
|
|
@@ -932,19 +972,33 @@ function evictStorageAndRetry(error, onyxMethod) {for (var _len = arguments.leng
|
|
|
932
972
|
*
|
|
933
973
|
* @param {String} key
|
|
934
974
|
* @param {*} value
|
|
975
|
+
* @param {Boolean} hasChanged
|
|
935
976
|
* @param {String} method
|
|
936
977
|
*/
|
|
937
|
-
function broadcastUpdate(key, value, method) {
|
|
978
|
+
function broadcastUpdate(key, value, hasChanged, method) {
|
|
938
979
|
// Logging properties only since values could be sensitive things we don't want to log
|
|
939
980
|
_Logger__WEBPACK_IMPORTED_MODULE_6__.logInfo(`${method}() called for key: ${key}${underscore__WEBPACK_IMPORTED_MODULE_2___default().isObject(value) ? ` properties: ${underscore__WEBPACK_IMPORTED_MODULE_2___default().keys(value).join(',')}` : ''}`);
|
|
940
981
|
|
|
941
982
|
// Update subscribers if the cached value has changed, or when the subscriber specifically requires
|
|
942
983
|
// all updates regardless of value changes (indicated by initWithStoredValues set to false).
|
|
943
|
-
|
|
944
|
-
|
|
984
|
+
if (hasChanged) {
|
|
985
|
+
_OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].set(key, value);
|
|
986
|
+
} else {
|
|
987
|
+
_OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].addToAccessedKeys(key);
|
|
988
|
+
}
|
|
989
|
+
|
|
945
990
|
notifySubscribersOnNextTick(key, value, (subscriber) => hasChanged || subscriber.initWithStoredValues === false);
|
|
946
991
|
}
|
|
947
992
|
|
|
993
|
+
/**
|
|
994
|
+
* @private
|
|
995
|
+
* @param {String} key
|
|
996
|
+
* @returns {Boolean}
|
|
997
|
+
*/
|
|
998
|
+
function hasPendingMergeForKey(key) {
|
|
999
|
+
return Boolean(mergeQueue[key]);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
948
1002
|
/**
|
|
949
1003
|
* Write a value to our store with the given key
|
|
950
1004
|
*
|
|
@@ -958,8 +1012,19 @@ function set(key, value) {
|
|
|
958
1012
|
return remove(key);
|
|
959
1013
|
}
|
|
960
1014
|
|
|
1015
|
+
if (hasPendingMergeForKey(key)) {
|
|
1016
|
+
_Logger__WEBPACK_IMPORTED_MODULE_6__.logAlert(`Onyx.set() called after Onyx.merge() for key: ${key}. It is recommended to use set() or merge() not both.`);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
const hasChanged = _OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].hasValueChanged(key, value);
|
|
1020
|
+
|
|
961
1021
|
// This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
|
|
962
|
-
broadcastUpdate(key, value, 'set');
|
|
1022
|
+
broadcastUpdate(key, value, hasChanged, 'set');
|
|
1023
|
+
|
|
1024
|
+
// If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
|
|
1025
|
+
if (!hasChanged) {
|
|
1026
|
+
return Promise.resolve();
|
|
1027
|
+
}
|
|
963
1028
|
|
|
964
1029
|
return _storage__WEBPACK_IMPORTED_MODULE_5__["default"].setItem(key, value).
|
|
965
1030
|
catch((error) => evictStorageAndRetry(error, set, key, value));
|
|
@@ -1002,11 +1067,11 @@ function multiSet(data) {
|
|
|
1002
1067
|
* Merges an array of changes with an existing value
|
|
1003
1068
|
*
|
|
1004
1069
|
* @private
|
|
1005
|
-
* @param {Array<*>} changes Array of changes that should be applied to the existing value
|
|
1006
1070
|
* @param {*} existingValue
|
|
1071
|
+
* @param {Array<*>} changes Array of changes that should be applied to the existing value
|
|
1007
1072
|
* @returns {*}
|
|
1008
1073
|
*/
|
|
1009
|
-
function applyMerge(
|
|
1074
|
+
function applyMerge(existingValue, changes) {
|
|
1010
1075
|
const lastChange = underscore__WEBPACK_IMPORTED_MODULE_2___default().last(changes);
|
|
1011
1076
|
|
|
1012
1077
|
if (underscore__WEBPACK_IMPORTED_MODULE_2___default().isArray(existingValue) || underscore__WEBPACK_IMPORTED_MODULE_2___default().isArray(lastChange)) {
|
|
@@ -1015,14 +1080,10 @@ function applyMerge(changes, existingValue) {
|
|
|
1015
1080
|
|
|
1016
1081
|
if (underscore__WEBPACK_IMPORTED_MODULE_2___default().isObject(existingValue) || underscore__WEBPACK_IMPORTED_MODULE_2___default().every(changes, (underscore__WEBPACK_IMPORTED_MODULE_2___default().isObject))) {
|
|
1017
1082
|
// Object values are merged one after the other
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
// Remove all first level keys that are explicitly set to null.
|
|
1024
|
-
return underscore__WEBPACK_IMPORTED_MODULE_2___default().omit(newData, (value) => underscore__WEBPACK_IMPORTED_MODULE_2___default().isNull(value));
|
|
1025
|
-
}, existingValue || {});
|
|
1083
|
+
// lodash adds a small overhead so we don't use it here
|
|
1084
|
+
// eslint-disable-next-line prefer-object-spread, rulesdir/prefer-underscore-method
|
|
1085
|
+
return underscore__WEBPACK_IMPORTED_MODULE_2___default().reduce(changes, (modifiedData, change) => (0,_fastMerge__WEBPACK_IMPORTED_MODULE_9__["default"])(modifiedData, change),
|
|
1086
|
+
existingValue || {});
|
|
1026
1087
|
}
|
|
1027
1088
|
|
|
1028
1089
|
// If we have anything else we can't merge it so we'll
|
|
@@ -1063,17 +1124,30 @@ function merge(key, changes) {
|
|
|
1063
1124
|
then((existingValue) => {
|
|
1064
1125
|
try {
|
|
1065
1126
|
// We first only merge the changes, so we can provide these to the native implementation (SQLite uses only delta changes in "JSON_PATCH" to merge)
|
|
1066
|
-
const batchedChanges = applyMerge(mergeQueue[key]);
|
|
1127
|
+
const batchedChanges = applyMerge(undefined, mergeQueue[key]);
|
|
1067
1128
|
|
|
1068
1129
|
// Clean up the write queue so we
|
|
1069
1130
|
// don't apply these changes again
|
|
1070
1131
|
delete mergeQueue[key];
|
|
1071
1132
|
|
|
1072
1133
|
// After that we merge the batched changes with the existing value
|
|
1073
|
-
|
|
1134
|
+
let modifiedData = applyMerge(existingValue, [batchedChanges]);
|
|
1135
|
+
|
|
1136
|
+
// For objects, the key for null values needs to be removed from the object to ensure the value will get removed from storage completely.
|
|
1137
|
+
// On native, SQLite will remove top-level keys that are null. To be consistent, we remove them on web too.
|
|
1138
|
+
if (!underscore__WEBPACK_IMPORTED_MODULE_2___default().isArray(modifiedData) && underscore__WEBPACK_IMPORTED_MODULE_2___default().isObject(modifiedData)) {
|
|
1139
|
+
modifiedData = underscore__WEBPACK_IMPORTED_MODULE_2___default().omit(modifiedData, (value) => underscore__WEBPACK_IMPORTED_MODULE_2___default().isNull(value));
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
const hasChanged = _OnyxCache__WEBPACK_IMPORTED_MODULE_4__["default"].hasValueChanged(key, modifiedData);
|
|
1074
1143
|
|
|
1075
1144
|
// This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
|
|
1076
|
-
broadcastUpdate(key, modifiedData, 'merge');
|
|
1145
|
+
broadcastUpdate(key, modifiedData, hasChanged, 'merge');
|
|
1146
|
+
|
|
1147
|
+
// If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
|
|
1148
|
+
if (!hasChanged) {
|
|
1149
|
+
return Promise.resolve();
|
|
1150
|
+
}
|
|
1077
1151
|
|
|
1078
1152
|
return _storage__WEBPACK_IMPORTED_MODULE_5__["default"].mergeItem(key, batchedChanges, modifiedData);
|
|
1079
1153
|
} catch (error) {
|
|
@@ -1084,15 +1158,6 @@ function merge(key, changes) {
|
|
|
1084
1158
|
});
|
|
1085
1159
|
}
|
|
1086
1160
|
|
|
1087
|
-
/**
|
|
1088
|
-
* @private
|
|
1089
|
-
* @param {String} key
|
|
1090
|
-
* @returns {Boolean}
|
|
1091
|
-
*/
|
|
1092
|
-
function hasPendingMergeForKey(key) {
|
|
1093
|
-
return Boolean(mergeQueue[key]);
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
1161
|
/**
|
|
1097
1162
|
* Merge user provided default key value pairs.
|
|
1098
1163
|
* @private
|
|
@@ -1411,7 +1476,8 @@ const Onyx = {
|
|
|
1411
1476
|
removeFromEvictionBlockList,
|
|
1412
1477
|
isSafeEvictionKey,
|
|
1413
1478
|
METHOD,
|
|
1414
|
-
setMemoryOnlyKeys
|
|
1479
|
+
setMemoryOnlyKeys,
|
|
1480
|
+
tryGetCachedValue
|
|
1415
1481
|
};
|
|
1416
1482
|
|
|
1417
1483
|
/**
|
|
@@ -2377,13 +2443,25 @@ function getDisplayName(component) {
|
|
|
2377
2443
|
// disconnected. It is a key value store with the format {[mapping.key]: connectionID}.
|
|
2378
2444
|
this.activeConnectionIDs = {};
|
|
2379
2445
|
|
|
2446
|
+
const cachedState = underscore__WEBPACK_IMPORTED_MODULE_2___default().reduce(mapOnyxToState, (resultObj, mapping, propertyName) => {
|
|
2447
|
+
const key = _Str__WEBPACK_IMPORTED_MODULE_3__.result(mapping.key, props);
|
|
2448
|
+
const value = _Onyx__WEBPACK_IMPORTED_MODULE_4__["default"].tryGetCachedValue(key, mapping);
|
|
2449
|
+
|
|
2450
|
+
if (value !== undefined) {
|
|
2451
|
+
// eslint-disable-next-line no-param-reassign
|
|
2452
|
+
resultObj[propertyName] = value;
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
return resultObj;
|
|
2456
|
+
}, {});
|
|
2457
|
+
|
|
2458
|
+
// If we have all the data we need, then we can render the component immediately
|
|
2459
|
+
cachedState.loading = underscore__WEBPACK_IMPORTED_MODULE_2___default().size(cachedState) < requiredKeysForInit.length;
|
|
2460
|
+
|
|
2380
2461
|
// Object holding the temporary initial state for the component while we load the various Onyx keys
|
|
2381
|
-
this.tempState =
|
|
2462
|
+
this.tempState = cachedState;
|
|
2382
2463
|
|
|
2383
|
-
this.state =
|
|
2384
|
-
// If there are no required keys for init then we can render the wrapped component immediately
|
|
2385
|
-
loading: requiredKeysForInit.length > 0
|
|
2386
|
-
};
|
|
2464
|
+
this.state = cachedState;
|
|
2387
2465
|
}
|
|
2388
2466
|
|
|
2389
2467
|
componentDidMount() {
|
|
@@ -2400,7 +2478,6 @@ function getDisplayName(component) {
|
|
|
2400
2478
|
underscore__WEBPACK_IMPORTED_MODULE_2___default().each(mapOnyxToState, (mapping, propertyName) => {
|
|
2401
2479
|
const previousKey = _Str__WEBPACK_IMPORTED_MODULE_3__.result(mapping.key, prevProps);
|
|
2402
2480
|
const newKey = _Str__WEBPACK_IMPORTED_MODULE_3__.result(mapping.key, this.props);
|
|
2403
|
-
|
|
2404
2481
|
if (previousKey !== newKey) {
|
|
2405
2482
|
_Onyx__WEBPACK_IMPORTED_MODULE_4__["default"].disconnect(this.activeConnectionIDs[previousKey], previousKey);
|
|
2406
2483
|
delete this.activeConnectionIDs[previousKey];
|
|
@@ -2428,6 +2505,16 @@ function getDisplayName(component) {
|
|
|
2428
2505
|
* @param {*} val
|
|
2429
2506
|
*/
|
|
2430
2507
|
setWithOnyxState(statePropertyName, val) {
|
|
2508
|
+
// We might have loaded the values for the onyx keys/mappings already from the cache.
|
|
2509
|
+
// In case we were able to load all the values upfront, the loading state will be false.
|
|
2510
|
+
// However, Onyx.js will always call setWithOnyxState, as it doesn't know that this implementation
|
|
2511
|
+
// already loaded the values from cache. Thus we have to check whether the value has changed
|
|
2512
|
+
// before we set the state to prevent unnecessary renders.
|
|
2513
|
+
const prevValue = this.state[statePropertyName];
|
|
2514
|
+
if (!this.state.loading && prevValue === val) {
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2431
2518
|
if (!this.state.loading) {
|
|
2432
2519
|
this.setState({ [statePropertyName]: val });
|
|
2433
2520
|
return;
|
|
@@ -2440,7 +2527,14 @@ function getDisplayName(component) {
|
|
|
2440
2527
|
return;
|
|
2441
2528
|
}
|
|
2442
2529
|
|
|
2443
|
-
|
|
2530
|
+
const stateUpdate = { ...this.tempState, loading: false };
|
|
2531
|
+
|
|
2532
|
+
// The state is set here manually, instead of using setState because setState is an async operation, meaning it might execute on the next tick,
|
|
2533
|
+
// or at a later point in the microtask queue. That can lead to a race condition where
|
|
2534
|
+
// setWithOnyxState is called before the state is actually set. This results in unreliable behavior when checking the loading state and has been mainly observed on fabric.
|
|
2535
|
+
this.state = stateUpdate;
|
|
2536
|
+
|
|
2537
|
+
this.setState(stateUpdate); // Trigger a render
|
|
2444
2538
|
delete this.tempState;
|
|
2445
2539
|
}
|
|
2446
2540
|
|