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.
@@ -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 _utils__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./utils */ "./lib/utils.js");
84
- /* harmony import */ var _batch__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./batch */ "./lib/batch.js");
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.setState((prevState) => {
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.setState((prevState) => {
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.setState((prevState) => {
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.setState((prevState) => {
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.setState((prevState) => {
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.setState((prevState) => {
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.setState((prevState) => {
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.setState((prevState) => {
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,_batch__WEBPACK_IMPORTED_MODULE_8__["default"])(() => {
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 = _utils__WEBPACK_IMPORTED_MODULE_9__["default"].removeNullObjectValues(value);
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) => (0,_fastMerge__WEBPACK_IMPORTED_MODULE_10__["default"])(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 = _utils__WEBPACK_IMPORTED_MODULE_9__["default"].removeNullObjectValues(applyMerge(existingValue, [batchedChanges]));
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 = _utils__WEBPACK_IMPORTED_MODULE_9__["default"].removeNullObjectValues(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 = (0,_fastMerge__WEBPACK_IMPORTED_MODULE_10__["default"])(asObject, defaultKeyStates);
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 _fastMerge__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./fastMerge */ "./lib/fastMerge.js");
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({}, (0,_fastMerge__WEBPACK_IMPORTED_MODULE_2__["default"])(this.storageMap, data));
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 _fastMerge__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../fastMerge */ "./lib/fastMerge.js");
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) ? (0,_fastMerge__WEBPACK_IMPORTED_MODULE_2__["default"])(prev, value) : value;
2319
- return (0,idb_keyval__WEBPACK_IMPORTED_MODULE_0__.promisifyRequest)(store.put(_utils__WEBPACK_IMPORTED_MODULE_3__["default"].removeNullObjectValues(newValue), key));
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 (underscore__WEBPACK_IMPORTED_MODULE_0___default().isArray(value) || !underscore__WEBPACK_IMPORTED_MODULE_0___default().isObject(value)) {
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 = underscore__WEBPACK_IMPORTED_MODULE_0___default().omit(value, (objectValue) => underscore__WEBPACK_IMPORTED_MODULE_0___default().isNull(objectValue));
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
- 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);} /**
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(mapOnyxToState, (resultObj, mapping, propertyName) => {
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
- const value = _Onyx__WEBPACK_IMPORTED_MODULE_4__["default"].tryGetCachedValue(key, mapping);
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 (value !== undefined && !_Onyx__WEBPACK_IMPORTED_MODULE_4__["default"].hasPendingMergeForKey(key)) {
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 externally by sendDataToConnection to prevent unnecessary renders while a component
2567
- * still in a loading state. The temporary initial state is saved to the component instance and setState()
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
- this.setState({ [statePropertyName]: val });
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
- // All state keys should exist and at least have a value of null
2592
- if (underscore__WEBPACK_IMPORTED_MODULE_2___default().some(requiredKeysForInit, (key) => underscore__WEBPACK_IMPORTED_MODULE_2___default().isUndefined(this.tempState[key]))) {
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
- // Leave untouched previous state to avoid data loss during pre-load updates.
2597
- // This handles case when setState was called before the setWithOnyxState.
2598
- // For example, when an Onyx property was updated by keyChanged before the call of the setWithOnyxState.
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 remainingTempState = underscore__WEBPACK_IMPORTED_MODULE_2___default().omit(this.tempState, underscore__WEBPACK_IMPORTED_MODULE_2___default().keys(prevState));
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
- return { ...remainingTempState, loading: false };
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
- delete this.tempState;
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, (value) => underscore__WEBPACK_IMPORTED_MODULE_2___default().isNull(value));
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
- // eslint-disable-next-line react/jsx-props-no-spreading
2677
- , _extends({}, propsToPass,
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 })));