react-native-onyx 1.0.82 → 1.0.84
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/API.md +27 -17
- package/README.md +28 -1
- package/dist/web.development.js +214 -144
- 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.d.ts +2 -10
- package/lib/Onyx.js +27 -15
- package/lib/OnyxCache.js +2 -2
- package/lib/storage/__mocks__/index.js +2 -2
- package/lib/storage/providers/IDBKeyVal.js +3 -4
- package/lib/types.d.ts +13 -4
- package/lib/utils.js +76 -2
- package/lib/withOnyx.d.ts +1 -0
- package/lib/withOnyx.js +115 -47
- package/package.json +1 -1
package/dist/web.development.js
CHANGED
|
@@ -77,11 +77,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
77
77
|
/* harmony import */ var _OnyxCache__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./OnyxCache */ "./lib/OnyxCache.js");
|
|
78
78
|
/* harmony import */ var _Str__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./Str */ "./lib/Str.js");
|
|
79
79
|
/* harmony import */ var _createDeferredTask__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./createDeferredTask */ "./lib/createDeferredTask.js");
|
|
80
|
-
/* harmony import */ var _fastMerge__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./fastMerge */ "./lib/fastMerge.js");
|
|
81
80
|
/* harmony import */ var _metrics_PerformanceUtils__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./metrics/PerformanceUtils */ "./lib/metrics/PerformanceUtils.js");
|
|
82
81
|
/* harmony import */ var _storage__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./storage */ "./lib/storage/index.web.js");
|
|
83
|
-
/* harmony import */ var
|
|
84
|
-
/* harmony import */ var
|
|
82
|
+
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./utils */ "./lib/utils.js");
|
|
83
|
+
/* harmony import */ var _batch__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./batch */ "./lib/batch.js");
|
|
85
84
|
/* eslint-disable no-continue */
|
|
86
85
|
|
|
87
86
|
|
|
@@ -94,7 +93,6 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
94
93
|
|
|
95
94
|
|
|
96
95
|
|
|
97
|
-
|
|
98
96
|
// Method constants
|
|
99
97
|
const METHOD = {
|
|
100
98
|
SET: 'set',
|
|
@@ -499,7 +497,7 @@ function keysChanged(collectionKey, partialCollection) {let notifyRegularSubscib
|
|
|
499
497
|
// If the subscriber has a selector, then the component's state must only be updated with the data
|
|
500
498
|
// returned by the selector.
|
|
501
499
|
if (subscriber.selector) {
|
|
502
|
-
subscriber.withOnyxInstance.
|
|
500
|
+
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
503
501
|
const previousData = prevState[subscriber.statePropertyName];
|
|
504
502
|
const newData = reduceCollectionWithSelector(cachedCollection, subscriber.selector, subscriber.withOnyxInstance.state);
|
|
505
503
|
|
|
@@ -513,7 +511,7 @@ function keysChanged(collectionKey, partialCollection) {let notifyRegularSubscib
|
|
|
513
511
|
continue;
|
|
514
512
|
}
|
|
515
513
|
|
|
516
|
-
subscriber.withOnyxInstance.
|
|
514
|
+
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
517
515
|
const finalCollection = underscore__WEBPACK_IMPORTED_MODULE_1___default().clone(prevState[subscriber.statePropertyName] || {});
|
|
518
516
|
const dataKeys = underscore__WEBPACK_IMPORTED_MODULE_1___default().keys(partialCollection);
|
|
519
517
|
for (let j = 0; j < dataKeys.length; j++) {
|
|
@@ -542,7 +540,7 @@ function keysChanged(collectionKey, partialCollection) {let notifyRegularSubscib
|
|
|
542
540
|
// returned by the selector and the state should only change when the subset of data changes from what
|
|
543
541
|
// it was previously.
|
|
544
542
|
if (subscriber.selector) {
|
|
545
|
-
subscriber.withOnyxInstance.
|
|
543
|
+
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
546
544
|
const prevData = prevState[subscriber.statePropertyName];
|
|
547
545
|
const newData = getSubsetOfData(cachedCollection[subscriber.key], subscriber.selector, subscriber.withOnyxInstance.state);
|
|
548
546
|
if (!(0,fast_equals__WEBPACK_IMPORTED_MODULE_0__.deepEqual)(prevData, newData)) {
|
|
@@ -557,9 +555,14 @@ function keysChanged(collectionKey, partialCollection) {let notifyRegularSubscib
|
|
|
557
555
|
continue;
|
|
558
556
|
}
|
|
559
557
|
|
|
560
|
-
subscriber.withOnyxInstance.
|
|
558
|
+
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
561
559
|
const data = cachedCollection[subscriber.key];
|
|
562
560
|
const previousData = prevState[subscriber.statePropertyName];
|
|
561
|
+
|
|
562
|
+
// Avoids triggering unnecessary re-renders when feeding empty objects
|
|
563
|
+
if (_utils__WEBPACK_IMPORTED_MODULE_8__["default"].areObjectsEmpty(data, previousData)) {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
563
566
|
if (data === previousData) {
|
|
564
567
|
return null;
|
|
565
568
|
}
|
|
@@ -632,7 +635,7 @@ function keyChanged(key, data, canUpdateSubscriber) {let notifyRegularSubscibers
|
|
|
632
635
|
// If the subscriber has a selector, then the consumer of this data must only be given the data
|
|
633
636
|
// returned by the selector and only when the selected data has changed.
|
|
634
637
|
if (subscriber.selector) {
|
|
635
|
-
subscriber.withOnyxInstance.
|
|
638
|
+
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
636
639
|
const prevData = prevState[subscriber.statePropertyName];
|
|
637
640
|
const newData = {
|
|
638
641
|
[key]: getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state)
|
|
@@ -652,7 +655,7 @@ function keyChanged(key, data, canUpdateSubscriber) {let notifyRegularSubscibers
|
|
|
652
655
|
continue;
|
|
653
656
|
}
|
|
654
657
|
|
|
655
|
-
subscriber.withOnyxInstance.
|
|
658
|
+
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
656
659
|
const collection = prevState[subscriber.statePropertyName] || {};
|
|
657
660
|
const newCollection = {
|
|
658
661
|
...collection,
|
|
@@ -669,7 +672,7 @@ function keyChanged(key, data, canUpdateSubscriber) {let notifyRegularSubscibers
|
|
|
669
672
|
// If the subscriber has a selector, then the component's state must only be updated with the data
|
|
670
673
|
// returned by the selector and only if the selected data has changed.
|
|
671
674
|
if (subscriber.selector) {
|
|
672
|
-
subscriber.withOnyxInstance.
|
|
675
|
+
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
673
676
|
const previousValue = getSubsetOfData(prevState[subscriber.statePropertyName], subscriber.selector, subscriber.withOnyxInstance.state);
|
|
674
677
|
const newValue = getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state);
|
|
675
678
|
if (!(0,fast_equals__WEBPACK_IMPORTED_MODULE_0__.deepEqual)(previousValue, newValue)) {
|
|
@@ -683,8 +686,13 @@ function keyChanged(key, data, canUpdateSubscriber) {let notifyRegularSubscibers
|
|
|
683
686
|
}
|
|
684
687
|
|
|
685
688
|
// If we did not match on a collection key then we just set the new data to the state property
|
|
686
|
-
subscriber.withOnyxInstance.
|
|
689
|
+
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
687
690
|
const previousData = prevState[subscriber.statePropertyName];
|
|
691
|
+
|
|
692
|
+
// Avoids triggering unnecessary re-renders when feeding empty objects
|
|
693
|
+
if (_utils__WEBPACK_IMPORTED_MODULE_8__["default"].areObjectsEmpty(data, previousData)) {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
688
696
|
if (previousData === data) {
|
|
689
697
|
return null;
|
|
690
698
|
}
|
|
@@ -812,6 +820,9 @@ function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
|
|
|
812
820
|
* The sourceData and withOnyx state are passed to the selector and should return the simplified data. Using this setting on `withOnyx` can have very positive
|
|
813
821
|
* performance benefits because the component will only re-render when the subset of data changes. Otherwise, any change of data on any property would normally
|
|
814
822
|
* cause the component to re-render (and that can be expensive from a performance standpoint).
|
|
823
|
+
* @param {String | Number | Boolean | Object} [mapping.initialValue] THIS PARAM IS ONLY USED WITH withOnyx().
|
|
824
|
+
* If included, this will be passed to the component so that something can be rendered while data is being fetched from the DB.
|
|
825
|
+
* Note that it will not cause the component to have the loading prop set to true. |
|
|
815
826
|
* @returns {Number} an ID to use when calling disconnect
|
|
816
827
|
*/
|
|
817
828
|
function connect(mapping) {
|
|
@@ -931,7 +942,7 @@ function maybeFlushBatchUpdates() {
|
|
|
931
942
|
const updatesCopy = batchUpdatesQueue;
|
|
932
943
|
batchUpdatesQueue = [];
|
|
933
944
|
batchUpdatesPromise = null;
|
|
934
|
-
(0,
|
|
945
|
+
(0,_batch__WEBPACK_IMPORTED_MODULE_9__["default"])(() => {
|
|
935
946
|
updatesCopy.forEach((applyUpdates) => {
|
|
936
947
|
applyUpdates();
|
|
937
948
|
});
|
|
@@ -1092,7 +1103,7 @@ function set(key, value) {
|
|
|
1092
1103
|
_Logger__WEBPACK_IMPORTED_MODULE_5__.logAlert(`Onyx.set() called after Onyx.merge() for key: ${key}. It is recommended to use set() or merge() not both.`);
|
|
1093
1104
|
}
|
|
1094
1105
|
|
|
1095
|
-
const valueWithNullRemoved =
|
|
1106
|
+
const valueWithNullRemoved = _utils__WEBPACK_IMPORTED_MODULE_8__["default"].removeNullObjectValues(value);
|
|
1096
1107
|
|
|
1097
1108
|
const hasChanged = _OnyxCache__WEBPACK_IMPORTED_MODULE_3__["default"].hasValueChanged(key, valueWithNullRemoved);
|
|
1098
1109
|
|
|
@@ -1162,7 +1173,7 @@ function applyMerge(existingValue, changes) {
|
|
|
1162
1173
|
// Object values are merged one after the other
|
|
1163
1174
|
// lodash adds a small overhead so we don't use it here
|
|
1164
1175
|
// eslint-disable-next-line prefer-object-spread, rulesdir/prefer-underscore-method
|
|
1165
|
-
return underscore__WEBPACK_IMPORTED_MODULE_1___default().reduce(changes, (modifiedData, change) =>
|
|
1176
|
+
return underscore__WEBPACK_IMPORTED_MODULE_1___default().reduce(changes, (modifiedData, change) => _utils__WEBPACK_IMPORTED_MODULE_8__["default"].fastMerge(modifiedData, change),
|
|
1166
1177
|
existingValue || {});
|
|
1167
1178
|
}
|
|
1168
1179
|
|
|
@@ -1211,14 +1222,14 @@ function merge(key, changes) {
|
|
|
1211
1222
|
delete mergeQueuePromise[key];
|
|
1212
1223
|
|
|
1213
1224
|
// After that we merge the batched changes with the existing value
|
|
1214
|
-
const modifiedData =
|
|
1225
|
+
const modifiedData = _utils__WEBPACK_IMPORTED_MODULE_8__["default"].removeNullObjectValues(applyMerge(existingValue, [batchedChanges]));
|
|
1215
1226
|
|
|
1216
1227
|
// On native platforms we use SQLite which utilises JSON_PATCH to merge changes.
|
|
1217
1228
|
// JSON_PATCH generally removes top-level nullish values from the stored object.
|
|
1218
1229
|
// When there is no existing value though, SQLite will just insert the changes as a new value and thus the top-level nullish values won't be removed.
|
|
1219
1230
|
// Therefore we need to remove nullish values from the `batchedChanges` which are sent to the SQLite, if no existing value is present.
|
|
1220
1231
|
if (!existingValue) {
|
|
1221
|
-
batchedChanges =
|
|
1232
|
+
batchedChanges = _utils__WEBPACK_IMPORTED_MODULE_8__["default"].removeNullObjectValues(batchedChanges);
|
|
1222
1233
|
}
|
|
1223
1234
|
|
|
1224
1235
|
const hasChanged = _OnyxCache__WEBPACK_IMPORTED_MODULE_3__["default"].hasValueChanged(key, modifiedData);
|
|
@@ -1252,7 +1263,7 @@ function initializeWithDefaultKeyStates() {
|
|
|
1252
1263
|
then((pairs) => {
|
|
1253
1264
|
const asObject = underscore__WEBPACK_IMPORTED_MODULE_1___default().object(pairs);
|
|
1254
1265
|
|
|
1255
|
-
const merged =
|
|
1266
|
+
const merged = _utils__WEBPACK_IMPORTED_MODULE_8__["default"].fastMerge(asObject, defaultKeyStates);
|
|
1256
1267
|
_OnyxCache__WEBPACK_IMPORTED_MODULE_3__["default"].merge(merged);
|
|
1257
1268
|
underscore__WEBPACK_IMPORTED_MODULE_1___default().each(merged, (val, key) => keyChanged(key, val));
|
|
1258
1269
|
});
|
|
@@ -1637,7 +1648,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
1637
1648
|
/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(underscore__WEBPACK_IMPORTED_MODULE_0__);
|
|
1638
1649
|
/* harmony import */ var fast_equals__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fast-equals */ "fast-equals");
|
|
1639
1650
|
/* harmony import */ var fast_equals__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fast_equals__WEBPACK_IMPORTED_MODULE_1__);
|
|
1640
|
-
/* harmony import */ var
|
|
1651
|
+
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils */ "./lib/utils.js");
|
|
1641
1652
|
|
|
1642
1653
|
|
|
1643
1654
|
|
|
@@ -1759,7 +1770,7 @@ class OnyxCache {
|
|
|
1759
1770
|
|
|
1760
1771
|
// lodash adds a small overhead so we don't use it here
|
|
1761
1772
|
// eslint-disable-next-line prefer-object-spread, rulesdir/prefer-underscore-method
|
|
1762
|
-
this.storageMap = Object.assign({},
|
|
1773
|
+
this.storageMap = Object.assign({}, _utils__WEBPACK_IMPORTED_MODULE_2__["default"].fastMerge(this.storageMap, data));
|
|
1763
1774
|
|
|
1764
1775
|
const storageKeys = this.getAllKeys();
|
|
1765
1776
|
const mergedKeys = underscore__WEBPACK_IMPORTED_MODULE_0___default().keys(data);
|
|
@@ -1950,87 +1961,6 @@ function createDeferredTask() {
|
|
|
1950
1961
|
|
|
1951
1962
|
/***/ }),
|
|
1952
1963
|
|
|
1953
|
-
/***/ "./lib/fastMerge.js":
|
|
1954
|
-
/*!**************************!*\
|
|
1955
|
-
!*** ./lib/fastMerge.js ***!
|
|
1956
|
-
\**************************/
|
|
1957
|
-
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
1958
|
-
|
|
1959
|
-
"use strict";
|
|
1960
|
-
__webpack_require__.r(__webpack_exports__);
|
|
1961
|
-
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
1962
|
-
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
1963
|
-
/* harmony export */ });
|
|
1964
|
-
/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! underscore */ "underscore");
|
|
1965
|
-
/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(underscore__WEBPACK_IMPORTED_MODULE_0__);
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
// Mostly copied from https://medium.com/@lubaka.a/how-to-remove-lodash-performance-improvement-b306669ad0e1
|
|
1969
|
-
|
|
1970
|
-
/**
|
|
1971
|
-
* @param {mixed} val
|
|
1972
|
-
* @returns {boolean}
|
|
1973
|
-
*/
|
|
1974
|
-
function isMergeableObject(val) {
|
|
1975
|
-
const nonNullObject = val != null ? typeof val === 'object' : false;
|
|
1976
|
-
return nonNullObject &&
|
|
1977
|
-
Object.prototype.toString.call(val) !== '[object RegExp]' &&
|
|
1978
|
-
Object.prototype.toString.call(val) !== '[object Date]';
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
/**
|
|
1982
|
-
* @param {Object} target
|
|
1983
|
-
* @param {Object} source
|
|
1984
|
-
* @returns {Object}
|
|
1985
|
-
*/
|
|
1986
|
-
function mergeObject(target, source) {
|
|
1987
|
-
const destination = {};
|
|
1988
|
-
if (isMergeableObject(target)) {
|
|
1989
|
-
// lodash adds a small overhead so we don't use it here
|
|
1990
|
-
// eslint-disable-next-line rulesdir/prefer-underscore-method
|
|
1991
|
-
const targetKeys = Object.keys(target);
|
|
1992
|
-
for (let i = 0; i < targetKeys.length; ++i) {
|
|
1993
|
-
const key = targetKeys[i];
|
|
1994
|
-
destination[key] = target[key];
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
|
-
// lodash adds a small overhead so we don't use it here
|
|
1999
|
-
// eslint-disable-next-line rulesdir/prefer-underscore-method
|
|
2000
|
-
const sourceKeys = Object.keys(source);
|
|
2001
|
-
for (let i = 0; i < sourceKeys.length; ++i) {
|
|
2002
|
-
const key = sourceKeys[i];
|
|
2003
|
-
if (source[key] === undefined) {
|
|
2004
|
-
// eslint-disable-next-line no-continue
|
|
2005
|
-
continue;
|
|
2006
|
-
}
|
|
2007
|
-
if (!isMergeableObject(source[key]) || !target[key]) {
|
|
2008
|
-
destination[key] = source[key];
|
|
2009
|
-
} else {
|
|
2010
|
-
// eslint-disable-next-line no-use-before-define
|
|
2011
|
-
destination[key] = fastMerge(target[key], source[key]);
|
|
2012
|
-
}
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
return destination;
|
|
2016
|
-
}
|
|
2017
|
-
|
|
2018
|
-
/**
|
|
2019
|
-
* @param {Object|Array} target
|
|
2020
|
-
* @param {Object|Array} source
|
|
2021
|
-
* @returns {Object|Array}
|
|
2022
|
-
*/
|
|
2023
|
-
function fastMerge(target, source) {
|
|
2024
|
-
if (underscore__WEBPACK_IMPORTED_MODULE_0___default().isArray(source) || underscore__WEBPACK_IMPORTED_MODULE_0___default().isNull(source) || underscore__WEBPACK_IMPORTED_MODULE_0___default().isUndefined(source)) {
|
|
2025
|
-
return source;
|
|
2026
|
-
}
|
|
2027
|
-
return mergeObject(target, source);
|
|
2028
|
-
}
|
|
2029
|
-
|
|
2030
|
-
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (fastMerge);
|
|
2031
|
-
|
|
2032
|
-
/***/ }),
|
|
2033
|
-
|
|
2034
1964
|
/***/ "./lib/metrics/PerformanceUtils.js":
|
|
2035
1965
|
/*!*****************************************!*\
|
|
2036
1966
|
!*** ./lib/metrics/PerformanceUtils.js ***!
|
|
@@ -2266,9 +2196,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
2266
2196
|
/* harmony import */ var idb_keyval__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(idb_keyval__WEBPACK_IMPORTED_MODULE_0__);
|
|
2267
2197
|
/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! underscore */ "underscore");
|
|
2268
2198
|
/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(underscore__WEBPACK_IMPORTED_MODULE_1__);
|
|
2269
|
-
/* harmony import */ var
|
|
2270
|
-
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../utils */ "./lib/utils.js");
|
|
2271
|
-
|
|
2199
|
+
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../utils */ "./lib/utils.js");
|
|
2272
2200
|
|
|
2273
2201
|
|
|
2274
2202
|
|
|
@@ -2315,8 +2243,8 @@ const provider = {
|
|
|
2315
2243
|
return getValues.then((values) => {
|
|
2316
2244
|
const upsertMany = underscore__WEBPACK_IMPORTED_MODULE_1___default().map(pairs, (_ref2, index) => {let [key, value] = _ref2;
|
|
2317
2245
|
const prev = values[index];
|
|
2318
|
-
const newValue = underscore__WEBPACK_IMPORTED_MODULE_1___default().isObject(prev) ?
|
|
2319
|
-
return (0,idb_keyval__WEBPACK_IMPORTED_MODULE_0__.promisifyRequest)(store.put(
|
|
2246
|
+
const newValue = underscore__WEBPACK_IMPORTED_MODULE_1___default().isObject(prev) ? _utils__WEBPACK_IMPORTED_MODULE_2__["default"].fastMerge(prev, value) : value;
|
|
2247
|
+
return (0,idb_keyval__WEBPACK_IMPORTED_MODULE_0__.promisifyRequest)(store.put(_utils__WEBPACK_IMPORTED_MODULE_2__["default"].removeNullObjectValues(newValue), key));
|
|
2320
2248
|
});
|
|
2321
2249
|
return Promise.all(upsertMany);
|
|
2322
2250
|
});
|
|
@@ -2416,6 +2344,79 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
2416
2344
|
/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(underscore__WEBPACK_IMPORTED_MODULE_0__);
|
|
2417
2345
|
|
|
2418
2346
|
|
|
2347
|
+
function areObjectsEmpty(a, b) {
|
|
2348
|
+
return (
|
|
2349
|
+
typeof a === 'object' &&
|
|
2350
|
+
typeof b === 'object' &&
|
|
2351
|
+
underscore__WEBPACK_IMPORTED_MODULE_0__.isEmpty(a) &&
|
|
2352
|
+
underscore__WEBPACK_IMPORTED_MODULE_0__.isEmpty(b));
|
|
2353
|
+
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
// Mostly copied from https://medium.com/@lubaka.a/how-to-remove-lodash-performance-improvement-b306669ad0e1
|
|
2357
|
+
|
|
2358
|
+
/**
|
|
2359
|
+
* @param {mixed} val
|
|
2360
|
+
* @returns {boolean}
|
|
2361
|
+
*/
|
|
2362
|
+
function isMergeableObject(val) {
|
|
2363
|
+
const nonNullObject = val != null ? typeof val === 'object' : false;
|
|
2364
|
+
return nonNullObject &&
|
|
2365
|
+
Object.prototype.toString.call(val) !== '[object RegExp]' &&
|
|
2366
|
+
Object.prototype.toString.call(val) !== '[object Date]';
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
/**
|
|
2370
|
+
* @param {Object} target
|
|
2371
|
+
* @param {Object} source
|
|
2372
|
+
* @returns {Object}
|
|
2373
|
+
*/
|
|
2374
|
+
function mergeObject(target, source) {
|
|
2375
|
+
const destination = {};
|
|
2376
|
+
if (isMergeableObject(target)) {
|
|
2377
|
+
// lodash adds a small overhead so we don't use it here
|
|
2378
|
+
// eslint-disable-next-line rulesdir/prefer-underscore-method
|
|
2379
|
+
const targetKeys = Object.keys(target);
|
|
2380
|
+
for (let i = 0; i < targetKeys.length; ++i) {
|
|
2381
|
+
const key = targetKeys[i];
|
|
2382
|
+
destination[key] = target[key];
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
// lodash adds a small overhead so we don't use it here
|
|
2387
|
+
// eslint-disable-next-line rulesdir/prefer-underscore-method
|
|
2388
|
+
const sourceKeys = Object.keys(source);
|
|
2389
|
+
for (let i = 0; i < sourceKeys.length; ++i) {
|
|
2390
|
+
const key = sourceKeys[i];
|
|
2391
|
+
if (source[key] === undefined) {
|
|
2392
|
+
// eslint-disable-next-line no-continue
|
|
2393
|
+
continue;
|
|
2394
|
+
}
|
|
2395
|
+
if (!isMergeableObject(source[key]) || !target[key]) {
|
|
2396
|
+
destination[key] = source[key];
|
|
2397
|
+
} else {
|
|
2398
|
+
// eslint-disable-next-line no-use-before-define
|
|
2399
|
+
destination[key] = fastMerge(target[key], source[key]);
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
return destination;
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
/**
|
|
2407
|
+
* @param {Object|Array} target
|
|
2408
|
+
* @param {Object|Array} source
|
|
2409
|
+
* @returns {Object|Array}
|
|
2410
|
+
*/
|
|
2411
|
+
function fastMerge(target, source) {
|
|
2412
|
+
// lodash adds a small overhead so we don't use it here
|
|
2413
|
+
// eslint-disable-next-line rulesdir/prefer-underscore-method
|
|
2414
|
+
if (underscore__WEBPACK_IMPORTED_MODULE_0__.isArray(source) || underscore__WEBPACK_IMPORTED_MODULE_0__.isNull(source) || underscore__WEBPACK_IMPORTED_MODULE_0__.isUndefined(source)) {
|
|
2415
|
+
return source;
|
|
2416
|
+
}
|
|
2417
|
+
return mergeObject(target, source);
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2419
2420
|
/**
|
|
2420
2421
|
* We generally want to remove top-level nullish values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk.
|
|
2421
2422
|
* On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values.
|
|
@@ -2427,16 +2428,16 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
2427
2428
|
* @returns {*}
|
|
2428
2429
|
*/
|
|
2429
2430
|
function removeNullObjectValues(value) {
|
|
2430
|
-
if (
|
|
2431
|
+
if (underscore__WEBPACK_IMPORTED_MODULE_0__.isArray(value) || !underscore__WEBPACK_IMPORTED_MODULE_0__.isObject(value)) {
|
|
2431
2432
|
return value;
|
|
2432
2433
|
}
|
|
2433
2434
|
|
|
2434
|
-
const objectWithoutNullObjectValues =
|
|
2435
|
+
const objectWithoutNullObjectValues = underscore__WEBPACK_IMPORTED_MODULE_0__.omit(value, (objectValue) => underscore__WEBPACK_IMPORTED_MODULE_0__.isNull(objectValue));
|
|
2435
2436
|
|
|
2436
2437
|
return objectWithoutNullObjectValues;
|
|
2437
2438
|
}
|
|
2438
2439
|
|
|
2439
|
-
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ removeNullObjectValues });
|
|
2440
|
+
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ removeNullObjectValues, areObjectsEmpty, fastMerge });
|
|
2440
2441
|
|
|
2441
2442
|
/***/ }),
|
|
2442
2443
|
|
|
@@ -2459,7 +2460,8 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
2459
2460
|
/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(underscore__WEBPACK_IMPORTED_MODULE_2__);
|
|
2460
2461
|
/* harmony import */ var _Onyx__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./Onyx */ "./lib/Onyx.js");
|
|
2461
2462
|
/* harmony import */ var _Str__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Str */ "./lib/Str.js");
|
|
2462
|
-
|
|
2463
|
+
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./utils */ "./lib/utils.js");
|
|
2464
|
+
function _extends() {_extends = Object.assign ? Object.assign.bind() : function (target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i];for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {target[key] = source[key];}}}return target;};return _extends.apply(this, arguments);}function _defineProperty(obj, key, value) {key = _toPropertyKey(key);if (key in obj) {Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });} else {obj[key] = value;}return obj;}function _toPropertyKey(arg) {var key = _toPrimitive(arg, "string");return typeof key === "symbol" ? key : String(key);}function _toPrimitive(input, hint) {if (typeof input !== "object" || input === null) return input;var prim = input[Symbol.toPrimitive];if (prim !== undefined) {var res = prim.call(input, hint || "default");if (typeof res !== "object") return res;throw new TypeError("@@toPrimitive must return a primitive value.");}return (hint === "string" ? String : Number)(input);} /**
|
|
2463
2465
|
* This is a higher order component that provides the ability to map a state property directly to
|
|
2464
2466
|
* something in Onyx (a key/value store). That way, as soon as data in Onyx changes, the state will be set and the view
|
|
2465
2467
|
* will automatically change to reflect the new data.
|
|
@@ -2470,6 +2472,7 @@ function _extends() {_extends = Object.assign ? Object.assign.bind() : function
|
|
|
2470
2472
|
|
|
2471
2473
|
|
|
2472
2474
|
|
|
2475
|
+
|
|
2473
2476
|
/**
|
|
2474
2477
|
* Returns the display name of a component
|
|
2475
2478
|
*
|
|
@@ -2480,7 +2483,7 @@ function getDisplayName(component) {
|
|
|
2480
2483
|
return component.displayName || component.name || 'Component';
|
|
2481
2484
|
}
|
|
2482
2485
|
|
|
2483
|
-
/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(mapOnyxToState) {
|
|
2486
|
+
/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(mapOnyxToState) {let shouldDelayUpdates = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
2484
2487
|
// A list of keys that must be present in tempState before we can render the WrappedComponent
|
|
2485
2488
|
const requiredKeysForInit = underscore__WEBPACK_IMPORTED_MODULE_2___default().chain(mapOnyxToState).
|
|
2486
2489
|
omit((config) => config.initWithStoredValues === false).
|
|
@@ -2489,18 +2492,26 @@ function getDisplayName(component) {
|
|
|
2489
2492
|
return (WrappedComponent) => {
|
|
2490
2493
|
const displayName = getDisplayName(WrappedComponent);
|
|
2491
2494
|
class withOnyx extends (react__WEBPACK_IMPORTED_MODULE_1___default().Component) {
|
|
2492
|
-
constructor(props) {
|
|
2493
|
-
super(props);
|
|
2494
2495
|
|
|
2496
|
+
|
|
2497
|
+
constructor(props) {
|
|
2498
|
+
super(props);_defineProperty(this, "pendingSetStates", []);
|
|
2499
|
+
this.shouldDelayUpdates = shouldDelayUpdates;
|
|
2495
2500
|
this.setWithOnyxState = this.setWithOnyxState.bind(this);
|
|
2501
|
+
this.flushPendingSetStates = this.flushPendingSetStates.bind(this);
|
|
2496
2502
|
|
|
2497
2503
|
// This stores all the Onyx connection IDs to be used when the component unmounts so everything can be
|
|
2498
2504
|
// disconnected. It is a key value store with the format {[mapping.key]: connectionID}.
|
|
2499
2505
|
this.activeConnectionIDs = {};
|
|
2500
2506
|
|
|
2501
|
-
const cachedState = underscore__WEBPACK_IMPORTED_MODULE_2___default().reduce(
|
|
2507
|
+
const cachedState = underscore__WEBPACK_IMPORTED_MODULE_2___default().reduce(
|
|
2508
|
+
mapOnyxToState,
|
|
2509
|
+
(resultObj, mapping, propertyName) => {
|
|
2502
2510
|
const key = _Str__WEBPACK_IMPORTED_MODULE_3__.result(mapping.key, props);
|
|
2503
|
-
|
|
2511
|
+
let value = _Onyx__WEBPACK_IMPORTED_MODULE_4__["default"].tryGetCachedValue(key, mapping);
|
|
2512
|
+
if (!value && mapping.initialValue) {
|
|
2513
|
+
value = mapping.initialValue;
|
|
2514
|
+
}
|
|
2504
2515
|
|
|
2505
2516
|
/**
|
|
2506
2517
|
* If we have a pending merge for a key it could mean that data is being set via Onyx.merge() and someone expects a component to have this data immediately.
|
|
@@ -2513,13 +2524,19 @@ function getDisplayName(component) {
|
|
|
2513
2524
|
* In reality, Onyx.merge() will only update the subscriber after all merges have been batched and the previous value is retrieved via a get() (returns a promise).
|
|
2514
2525
|
* So, we won't use the cache optimization here as it will lead us to arbitrarily defer various actions in the application code.
|
|
2515
2526
|
*/
|
|
2516
|
-
if (
|
|
2527
|
+
if (
|
|
2528
|
+
value !== undefined &&
|
|
2529
|
+
!_Onyx__WEBPACK_IMPORTED_MODULE_4__["default"].hasPendingMergeForKey(key) ||
|
|
2530
|
+
mapping.allowStaleData)
|
|
2531
|
+
{
|
|
2517
2532
|
// eslint-disable-next-line no-param-reassign
|
|
2518
2533
|
resultObj[propertyName] = value;
|
|
2519
2534
|
}
|
|
2520
2535
|
|
|
2521
2536
|
return resultObj;
|
|
2522
|
-
},
|
|
2537
|
+
},
|
|
2538
|
+
{});
|
|
2539
|
+
|
|
2523
2540
|
|
|
2524
2541
|
// If we have all the data we need, then we can render the component immediately
|
|
2525
2542
|
cachedState.loading = underscore__WEBPACK_IMPORTED_MODULE_2___default().size(cachedState) < requiredKeysForInit.length;
|
|
@@ -2562,47 +2579,86 @@ function getDisplayName(component) {
|
|
|
2562
2579
|
});
|
|
2563
2580
|
}
|
|
2564
2581
|
|
|
2582
|
+
setStateProxy(modifier) {
|
|
2583
|
+
if (this.shouldDelayUpdates) {
|
|
2584
|
+
this.pendingSetStates.push(modifier);
|
|
2585
|
+
} else {
|
|
2586
|
+
this.setState(modifier);
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2565
2590
|
/**
|
|
2566
|
-
* This method is used
|
|
2567
|
-
* still in a loading state. The temporary initial state is saved to the
|
|
2591
|
+
* This method is used by the internal raw Onyx `sendDataToConnection`, it is designed to prevent unnecessary renders while a component
|
|
2592
|
+
* still in a "loading" (read "mounting") state. The temporary initial state is saved to the HOC instance and setState()
|
|
2568
2593
|
* only called once all the necessary data has been collected.
|
|
2569
2594
|
*
|
|
2595
|
+
* There is however the possibility the component could have been updated by a call to setState()
|
|
2596
|
+
* before the data was "initially" collected. A race condition.
|
|
2597
|
+
* For example some update happened on some key, while onyx was still gathering the initial hydration data.
|
|
2598
|
+
* This update is disptached directly to setStateProxy and therefore the component has the most up-to-date data
|
|
2599
|
+
*
|
|
2600
|
+
* This is a design flaw in Onyx itself as dispatching updates before initial hydration is not a correct event flow.
|
|
2601
|
+
* We however need to workaround this issue in the HOC. The addition of initialValue makes things even more complex,
|
|
2602
|
+
* since you cannot be really sure if the component has been updated before or after the initial hydration. Therefore if
|
|
2603
|
+
* initialValue is there, we just check if the update is different than that and then try to handle it as best as we can.
|
|
2604
|
+
*
|
|
2570
2605
|
* @param {String} statePropertyName
|
|
2571
2606
|
* @param {*} val
|
|
2572
2607
|
*/
|
|
2573
2608
|
setWithOnyxState(statePropertyName, val) {
|
|
2574
|
-
// We might have loaded the values for the onyx keys/mappings already from the cache.
|
|
2575
|
-
// In case we were able to load all the values upfront, the loading state will be false.
|
|
2576
|
-
// However, Onyx.js will always call setWithOnyxState, as it doesn't know that this implementation
|
|
2577
|
-
// already loaded the values from cache. Thus we have to check whether the value has changed
|
|
2578
|
-
// before we set the state to prevent unnecessary renders.
|
|
2579
2609
|
const prevValue = this.state[statePropertyName];
|
|
2580
|
-
if (!this.state.loading && prevValue === val) {
|
|
2581
|
-
return;
|
|
2582
|
-
}
|
|
2583
2610
|
|
|
2611
|
+
// If the component is not loading (read "mounting"), then we can just update the state
|
|
2584
2612
|
if (!this.state.loading) {
|
|
2585
|
-
|
|
2613
|
+
// Performance optimization, do not trigger update with same values
|
|
2614
|
+
if (prevValue === val || _utils__WEBPACK_IMPORTED_MODULE_5__["default"].areObjectsEmpty(prevValue, val)) {
|
|
2615
|
+
return;
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
this.setStateProxy({ [statePropertyName]: val });
|
|
2586
2619
|
return;
|
|
2587
2620
|
}
|
|
2588
2621
|
|
|
2589
2622
|
this.tempState[statePropertyName] = val;
|
|
2590
2623
|
|
|
2591
|
-
//
|
|
2592
|
-
|
|
2624
|
+
// If some key does not have a value yet, do not update the state yet
|
|
2625
|
+
const tempStateIsMissingKey = underscore__WEBPACK_IMPORTED_MODULE_2___default().some(requiredKeysForInit, (key) => underscore__WEBPACK_IMPORTED_MODULE_2___default().isUndefined(this.tempState[key]));
|
|
2626
|
+
if (tempStateIsMissingKey) {
|
|
2593
2627
|
return;
|
|
2594
2628
|
}
|
|
2595
2629
|
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2630
|
+
const stateUpdate = { ...this.tempState };
|
|
2631
|
+
delete this.tempState;
|
|
2632
|
+
|
|
2633
|
+
// Full of hacky workarounds to prevent the race condition described above.
|
|
2599
2634
|
this.setState((prevState) => {
|
|
2600
|
-
const
|
|
2635
|
+
const finalState = underscore__WEBPACK_IMPORTED_MODULE_2___default().reduce(stateUpdate, (result, value, key) => {
|
|
2636
|
+
if (key === 'loading') {
|
|
2637
|
+
return result;
|
|
2638
|
+
}
|
|
2601
2639
|
|
|
2602
|
-
|
|
2603
|
-
|
|
2640
|
+
const initialValue = mapOnyxToState[key].initialValue;
|
|
2641
|
+
|
|
2642
|
+
// If initialValue is there and the state contains something different it means
|
|
2643
|
+
// an update has already been received and we can discard the value we are trying to hydrate
|
|
2644
|
+
if (!underscore__WEBPACK_IMPORTED_MODULE_2___default().isUndefined(initialValue) && !underscore__WEBPACK_IMPORTED_MODULE_2___default().isUndefined(prevState[key]) && prevState[key] !== initialValue) {
|
|
2645
|
+
// eslint-disable-next-line no-param-reassign
|
|
2646
|
+
result[key] = prevState[key];
|
|
2647
|
+
|
|
2648
|
+
// if value is already there (without initial value) then we can discard the value we are trying to hydrate
|
|
2649
|
+
} else if (!underscore__WEBPACK_IMPORTED_MODULE_2___default().isUndefined(prevState[key])) {
|
|
2650
|
+
// eslint-disable-next-line no-param-reassign
|
|
2651
|
+
result[key] = prevState[key];
|
|
2652
|
+
} else {
|
|
2653
|
+
// eslint-disable-next-line no-param-reassign
|
|
2654
|
+
result[key] = value;
|
|
2655
|
+
}
|
|
2656
|
+
return result;
|
|
2657
|
+
}, {});
|
|
2604
2658
|
|
|
2605
|
-
|
|
2659
|
+
finalState.loading = false;
|
|
2660
|
+
return finalState;
|
|
2661
|
+
});
|
|
2606
2662
|
}
|
|
2607
2663
|
|
|
2608
2664
|
/**
|
|
@@ -2657,7 +2713,23 @@ function getDisplayName(component) {
|
|
|
2657
2713
|
});
|
|
2658
2714
|
}
|
|
2659
2715
|
|
|
2716
|
+
flushPendingSetStates() {
|
|
2717
|
+
if (!this.shouldDelayUpdates) {
|
|
2718
|
+
return;
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
this.shouldDelayUpdates = false;
|
|
2722
|
+
|
|
2723
|
+
this.pendingSetStates.forEach((modifier) => {
|
|
2724
|
+
this.setState(modifier);
|
|
2725
|
+
});
|
|
2726
|
+
this.pendingSetStates = [];
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2660
2729
|
render() {
|
|
2730
|
+
// Remove any null values so that React replaces them with default props
|
|
2731
|
+
const propsToPass = underscore__WEBPACK_IMPORTED_MODULE_2___default().omit(this.props, (underscore__WEBPACK_IMPORTED_MODULE_2___default().isNull));
|
|
2732
|
+
|
|
2661
2733
|
if (this.state.loading) {
|
|
2662
2734
|
return null;
|
|
2663
2735
|
}
|
|
@@ -2665,16 +2737,14 @@ function getDisplayName(component) {
|
|
|
2665
2737
|
// Remove any internal state properties used by withOnyx
|
|
2666
2738
|
// that should not be passed to a wrapped component
|
|
2667
2739
|
let stateToPass = underscore__WEBPACK_IMPORTED_MODULE_2___default().omit(this.state, 'loading');
|
|
2668
|
-
stateToPass = underscore__WEBPACK_IMPORTED_MODULE_2___default().omit(stateToPass, (
|
|
2669
|
-
|
|
2670
|
-
// Remove any null values so that React replaces them with default props
|
|
2671
|
-
const propsToPass = underscore__WEBPACK_IMPORTED_MODULE_2___default().omit(this.props, (value) => underscore__WEBPACK_IMPORTED_MODULE_2___default().isNull(value));
|
|
2740
|
+
stateToPass = underscore__WEBPACK_IMPORTED_MODULE_2___default().omit(stateToPass, (underscore__WEBPACK_IMPORTED_MODULE_2___default().isNull));
|
|
2672
2741
|
|
|
2673
2742
|
// Spreading props and state is necessary in an HOC where the data cannot be predicted
|
|
2674
2743
|
return /*#__PURE__*/(
|
|
2675
|
-
react__WEBPACK_IMPORTED_MODULE_1___default().createElement(WrappedComponent
|
|
2676
|
-
|
|
2677
|
-
|
|
2744
|
+
react__WEBPACK_IMPORTED_MODULE_1___default().createElement(WrappedComponent, _extends({
|
|
2745
|
+
markReadyForHydration: this.flushPendingSetStates
|
|
2746
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
2747
|
+
}, propsToPass,
|
|
2678
2748
|
|
|
2679
2749
|
stateToPass, {
|
|
2680
2750
|
ref: this.props.forwardedRef })));
|