react-native-onyx 1.0.19 → 1.0.20

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/README.md CHANGED
@@ -319,6 +319,9 @@ Sample output of `Onyx.printMetrics()`
319
319
  | 1.17sec | 2.20sec | 1.03sec | currentURL, / |
320
320
  ```
321
321
 
322
+ # Debug mode
323
+
324
+ It can be useful to log why Onyx is calling `setState()` on a particular React component so that we can understand which key changed, what changed about the value, and the connected component that ultimately rendered as a result. When used correctly this can help isolate problem areas and unnecessary renders in the code. To enable this feature, pass `debugSetState: true` to the config and grep JS console logs for `[Onyx-Debug]`.
322
325
 
323
326
  # Development
324
327
 
@@ -1,13 +1,13 @@
1
1
  (function webpackUniversalModuleDefinition(root, factory) {
2
2
  if(typeof exports === 'object' && typeof module === 'object')
3
- module.exports = factory(require("underscore"), require("expensify-common/lib/str"), require("lodash/get"), require("localforage"), require("react"));
3
+ module.exports = factory(require("underscore"), require("expensify-common/lib/str"), require("lodash/get"), require("localforage"), require("lodash/transform"), require("react"));
4
4
  else if(typeof define === 'function' && define.amd)
5
- define(["underscore", "expensify-common/lib/str", "lodash/get", "localforage", "react"], factory);
5
+ define(["underscore", "expensify-common/lib/str", "lodash/get", "localforage", "lodash/transform", "react"], factory);
6
6
  else if(typeof exports === 'object')
7
- exports["react-native-onyx/web"] = factory(require("underscore"), require("expensify-common/lib/str"), require("lodash/get"), require("localforage"), require("react"));
7
+ exports["react-native-onyx/web"] = factory(require("underscore"), require("expensify-common/lib/str"), require("lodash/get"), require("localforage"), require("lodash/transform"), require("react"));
8
8
  else
9
- root["react-native-onyx/web"] = factory(root["underscore"], root["expensify-common/lib/str"], root["lodash/get"], root["localforage"], root["react"]);
10
- })(self, (__WEBPACK_EXTERNAL_MODULE_underscore__, __WEBPACK_EXTERNAL_MODULE_expensify_common_lib_str__, __WEBPACK_EXTERNAL_MODULE_lodash_get__, __WEBPACK_EXTERNAL_MODULE_localforage__, __WEBPACK_EXTERNAL_MODULE_react__) => {
9
+ root["react-native-onyx/web"] = factory(root["underscore"], root["expensify-common/lib/str"], root["lodash/get"], root["localforage"], root["lodash/transform"], root["react"]);
10
+ })(self, (__WEBPACK_EXTERNAL_MODULE_underscore__, __WEBPACK_EXTERNAL_MODULE_expensify_common_lib_str__, __WEBPACK_EXTERNAL_MODULE_lodash_get__, __WEBPACK_EXTERNAL_MODULE_localforage__, __WEBPACK_EXTERNAL_MODULE_lodash_transform__, __WEBPACK_EXTERNAL_MODULE_react__) => {
11
11
  return /******/ (() => { // webpackBootstrap
12
12
  /******/ var __webpack_modules__ = ({
13
13
 
@@ -497,7 +497,8 @@ var _storage = _interopRequireDefault(__webpack_require__(/*! ./storage */ "./li
497
497
  var Logger = _interopRequireWildcard(__webpack_require__(/*! ./Logger */ "./lib/Logger.js"));
498
498
  var _OnyxCache = _interopRequireDefault(__webpack_require__(/*! ./OnyxCache */ "./lib/OnyxCache.js"));
499
499
  var _createDeferredTask = _interopRequireDefault(__webpack_require__(/*! ./createDeferredTask */ "./lib/createDeferredTask.js"));
500
- var _fastMerge = _interopRequireDefault(__webpack_require__(/*! ./fastMerge */ "./lib/fastMerge.js"));function _getRequireWildcardCache(nodeInterop) {if (typeof WeakMap !== "function") return null;var cacheBabelInterop = new WeakMap();var cacheNodeInterop = new WeakMap();return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) {return nodeInterop ? cacheNodeInterop : cacheBabelInterop;})(nodeInterop);}function _interopRequireWildcard(obj, nodeInterop) {if (!nodeInterop && obj && obj.__esModule) {return obj;}if (obj === null || typeof obj !== "object" && typeof obj !== "function") {return { default: obj };}var cache = _getRequireWildcardCache(nodeInterop);if (cache && cache.has(obj)) {return cache.get(obj);}var newObj = {};var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;for (var key in obj) {if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;if (desc && (desc.get || desc.set)) {Object.defineProperty(newObj, key, desc);} else {newObj[key] = obj[key];}}}newObj.default = obj;if (cache) {cache.set(obj, newObj);}return newObj;} /* eslint-disable no-continue */
500
+ var _fastMerge = _interopRequireDefault(__webpack_require__(/*! ./fastMerge */ "./lib/fastMerge.js"));
501
+ var PerformanceUtils = _interopRequireWildcard(__webpack_require__(/*! ./metrics/PerformanceUtils */ "./lib/metrics/PerformanceUtils.js"));function _getRequireWildcardCache(nodeInterop) {if (typeof WeakMap !== "function") return null;var cacheBabelInterop = new WeakMap();var cacheNodeInterop = new WeakMap();return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) {return nodeInterop ? cacheNodeInterop : cacheBabelInterop;})(nodeInterop);}function _interopRequireWildcard(obj, nodeInterop) {if (!nodeInterop && obj && obj.__esModule) {return obj;}if (obj === null || typeof obj !== "object" && typeof obj !== "function") {return { default: obj };}var cache = _getRequireWildcardCache(nodeInterop);if (cache && cache.has(obj)) {return cache.get(obj);}var newObj = {};var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;for (var key in obj) {if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;if (desc && (desc.get || desc.set)) {Object.defineProperty(newObj, key, desc);} else {newObj[key] = obj[key];}}}newObj.default = obj;if (cache) {cache.set(obj, newObj);}return newObj;} /* eslint-disable no-continue */
501
502
 
502
503
  // Keeps track of the last connectionID that was used so we can keep incrementing it
503
504
  var lastConnectionID = 0;
@@ -814,12 +815,13 @@ function keysChanged(collectionKey, partialCollection) {
814
815
  if (isSubscribedToCollectionKey) {
815
816
  subscriber.withOnyxInstance.setState(function (prevState) {
816
817
  var finalCollection = _underscore.default.clone(prevState[subscriber.statePropertyName] || {});
817
-
818
818
  var dataKeys = _underscore.default.keys(partialCollection);
819
819
  for (var _j = 0; _j < dataKeys.length; _j++) {
820
820
  var _dataKey = dataKeys[_j];
821
821
  finalCollection[_dataKey] = cachedCollection[_dataKey];
822
822
  }
823
+
824
+ PerformanceUtils.logSetStateCall(subscriber, prevState[subscriber.statePropertyName], finalCollection, 'keysChanged', collectionKey);
823
825
  return (0, _defineProperty2.default)({},
824
826
  subscriber.statePropertyName, finalCollection);
825
827
 
@@ -836,9 +838,18 @@ function keysChanged(collectionKey, partialCollection) {
836
838
  return "continue";
837
839
  }
838
840
 
839
- subscriber.withOnyxInstance.setState((0, _defineProperty2.default)({},
840
- subscriber.statePropertyName, cachedCollection[subscriber.key]));
841
+ subscriber.withOnyxInstance.setState(function (prevState) {
842
+ var data = cachedCollection[subscriber.key];
843
+ var previousData = prevState[subscriber.statePropertyName];
844
+ if (data === previousData) {
845
+ return null;
846
+ }
847
+
848
+ PerformanceUtils.logSetStateCall(subscriber, previousData, data, 'keysChanged', collectionKey);
849
+ return (0, _defineProperty2.default)({},
850
+ subscriber.statePropertyName, data);
841
851
 
852
+ });
842
853
  }
843
854
  }};for (var i = stateMappingKeys.length; i--;) {var _ret = _loop(i);if (_ret === "continue") continue;
844
855
  }
@@ -888,20 +899,30 @@ function keyChanged(key, data) {
888
899
  if (isCollectionKey(subscriber.key)) {
889
900
  subscriber.withOnyxInstance.setState(function (prevState) {
890
901
  var collection = prevState[subscriber.statePropertyName] || {};
891
- return (0, _defineProperty2.default)({},
892
- subscriber.statePropertyName, (0, _extends3.default)({},
902
+ var newCollection = (0, _extends3.default)({},
893
903
  collection, (0, _defineProperty2.default)({},
894
- key, data)));
904
+ key, data));
895
905
 
906
+ PerformanceUtils.logSetStateCall(subscriber, collection, newCollection, 'keyChanged', key);
907
+ return (0, _defineProperty2.default)({},
908
+ subscriber.statePropertyName, newCollection);
896
909
 
897
910
  });
898
911
  return "continue";
899
912
  }
900
913
 
901
914
  // If we did not match on a collection key then we just set the new data to the state property
902
- subscriber.withOnyxInstance.setState((0, _defineProperty2.default)({},
903
- subscriber.statePropertyName, data));
915
+ subscriber.withOnyxInstance.setState(function (prevState) {
916
+ var previousData = prevState[subscriber.statePropertyName];
917
+ if (previousData === data) {
918
+ return null;
919
+ }
920
+
921
+ PerformanceUtils.logSetStateCall(subscriber, previousData, data, 'keyChanged', key);
922
+ return (0, _defineProperty2.default)({},
923
+ subscriber.statePropertyName, data);
904
924
 
925
+ });
905
926
  return "continue";
906
927
  }
907
928
 
@@ -930,6 +951,7 @@ function sendDataToConnection(config, val, matchedKey) {
930
951
  }
931
952
 
932
953
  if (config.withOnyxInstance) {
954
+ PerformanceUtils.logSetStateCall(config, null, val, 'sendDataToConnection');
933
955
  config.withOnyxInstance.setWithOnyxState(config.statePropertyName, val);
934
956
  } else if (_underscore.default.isFunction(config.callback)) {
935
957
  config.callback(val, matchedKey);
@@ -1432,7 +1454,7 @@ function mergeCollection(collectionKey, collection) {
1432
1454
  */
1433
1455
  function update(data) {
1434
1456
  // First, validate the Onyx object is in the format we expect
1435
- _underscore.default.each(data, function (_ref3) {var onyxMethod = _ref3.onyxMethod,key = _ref3.key;
1457
+ _underscore.default.each(data, function (_ref5) {var onyxMethod = _ref5.onyxMethod,key = _ref5.key;
1436
1458
  if (!_underscore.default.contains(['clear', 'set', 'merge', 'mergecollection'], onyxMethod)) {
1437
1459
  throw new Error("Invalid onyxMethod " + onyxMethod + " in Onyx update.");
1438
1460
  }
@@ -1441,7 +1463,7 @@ function update(data) {
1441
1463
  }
1442
1464
  });
1443
1465
 
1444
- _underscore.default.each(data, function (_ref4) {var onyxMethod = _ref4.onyxMethod,key = _ref4.key,value = _ref4.value;
1466
+ _underscore.default.each(data, function (_ref6) {var onyxMethod = _ref6.onyxMethod,key = _ref6.key,value = _ref6.value;
1445
1467
  switch (onyxMethod) {
1446
1468
  case 'set':
1447
1469
  set(key, value);
@@ -1479,6 +1501,7 @@ function update(data) {
1479
1501
  * of Onyx running in different tabs/windows. Defaults to true for platforms that support local storage (web/desktop)
1480
1502
  * @param {String[]} [option.keysToDisableSyncEvents=[]] Contains keys for which
1481
1503
  * we want to disable sync event across tabs.
1504
+ * @param {Boolean} [options.debugSetState] Enables debugging setState() calls to connected components.
1482
1505
  * @example
1483
1506
  * Onyx.init({
1484
1507
  * keys: ONYXKEYS,
@@ -1495,13 +1518,18 @@ function init()
1495
1518
 
1496
1519
 
1497
1520
 
1498
- {var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},_ref5$keys = _ref5.keys,keys = _ref5$keys === void 0 ? {} : _ref5$keys,_ref5$initialKeyState = _ref5.initialKeyStates,initialKeyStates = _ref5$initialKeyState === void 0 ? {} : _ref5$initialKeyState,_ref5$safeEvictionKey = _ref5.safeEvictionKeys,safeEvictionKeys = _ref5$safeEvictionKey === void 0 ? [] : _ref5$safeEvictionKey,_ref5$maxCachedKeysCo = _ref5.maxCachedKeysCount,maxCachedKeysCount = _ref5$maxCachedKeysCo === void 0 ? 1000 : _ref5$maxCachedKeysCo,_ref5$captureMetrics = _ref5.captureMetrics,captureMetrics = _ref5$captureMetrics === void 0 ? false : _ref5$captureMetrics,_ref5$shouldSyncMulti = _ref5.shouldSyncMultipleInstances,shouldSyncMultipleInstances = _ref5$shouldSyncMulti === void 0 ? Boolean(__webpack_require__.g.localStorage) : _ref5$shouldSyncMulti,_ref5$keysToDisableSy = _ref5.keysToDisableSyncEvents,keysToDisableSyncEvents = _ref5$keysToDisableSy === void 0 ? [] : _ref5$keysToDisableSy;
1521
+
1522
+ {var _ref7 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},_ref7$keys = _ref7.keys,keys = _ref7$keys === void 0 ? {} : _ref7$keys,_ref7$initialKeyState = _ref7.initialKeyStates,initialKeyStates = _ref7$initialKeyState === void 0 ? {} : _ref7$initialKeyState,_ref7$safeEvictionKey = _ref7.safeEvictionKeys,safeEvictionKeys = _ref7$safeEvictionKey === void 0 ? [] : _ref7$safeEvictionKey,_ref7$maxCachedKeysCo = _ref7.maxCachedKeysCount,maxCachedKeysCount = _ref7$maxCachedKeysCo === void 0 ? 1000 : _ref7$maxCachedKeysCo,_ref7$captureMetrics = _ref7.captureMetrics,captureMetrics = _ref7$captureMetrics === void 0 ? false : _ref7$captureMetrics,_ref7$shouldSyncMulti = _ref7.shouldSyncMultipleInstances,shouldSyncMultipleInstances = _ref7$shouldSyncMulti === void 0 ? Boolean(__webpack_require__.g.localStorage) : _ref7$shouldSyncMulti,_ref7$keysToDisableSy = _ref7.keysToDisableSyncEvents,keysToDisableSyncEvents = _ref7$keysToDisableSy === void 0 ? [] : _ref7$keysToDisableSy,_ref7$debugSetState = _ref7.debugSetState,debugSetState = _ref7$debugSetState === void 0 ? false : _ref7$debugSetState;
1499
1523
  if (captureMetrics) {
1500
1524
  // The code here is only bundled and applied when the captureMetrics is set
1501
1525
  // eslint-disable-next-line no-use-before-define
1502
1526
  applyDecorators();
1503
1527
  }
1504
1528
 
1529
+ if (debugSetState) {
1530
+ PerformanceUtils.setShouldDebugSetState(true);
1531
+ }
1532
+
1505
1533
  if (maxCachedKeysCount > 0) {
1506
1534
  _OnyxCache.default.setRecentKeysLimit(maxCachedKeysCount);
1507
1535
  }
@@ -1954,6 +1982,78 @@ fastMerge;exports["default"] = _default;
1954
1982
 
1955
1983
  /***/ }),
1956
1984
 
1985
+ /***/ "./lib/metrics/PerformanceUtils.js":
1986
+ /*!*****************************************!*\
1987
+ !*** ./lib/metrics/PerformanceUtils.js ***!
1988
+ \*****************************************/
1989
+ /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
1990
+
1991
+ var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "./node_modules/@babel/runtime/helpers/interopRequireDefault.js");Object.defineProperty(exports, "__esModule", ({ value: true }));exports.logSetStateCall = logSetStateCall;exports.setShouldDebugSetState = setShouldDebugSetState;var _transform = _interopRequireDefault(__webpack_require__(/*! lodash/transform */ "lodash/transform"));
1992
+ var _underscore = _interopRequireDefault(__webpack_require__(/*! underscore */ "underscore"));
1993
+
1994
+ var debugSetState = false;
1995
+
1996
+ /**
1997
+ * @param {Boolean} debug
1998
+ */
1999
+ function setShouldDebugSetState(debug) {
2000
+ debugSetState = debug;
2001
+ }
2002
+
2003
+ /**
2004
+ * Deep diff between two objects. Useful for figuring out what changed about an object from one render to the next so
2005
+ * that state and props updates can be optimized.
2006
+ *
2007
+ * @param {Object} object
2008
+ * @param {Object} base
2009
+ * @return {Object}
2010
+ */
2011
+ function diffObject(object, base) {
2012
+ function changes(obj, comparisonObject) {
2013
+ return (0, _transform.default)(obj, function (result, value, key) {
2014
+ if (_underscore.default.isEqual(value, comparisonObject[key])) {
2015
+ return;
2016
+ }
2017
+
2018
+ // eslint-disable-next-line no-param-reassign
2019
+ result[key] = _underscore.default.isObject(value) && _underscore.default.isObject(comparisonObject[key]) ?
2020
+ changes(value, comparisonObject[key]) :
2021
+ value;
2022
+ });
2023
+ }
2024
+ return changes(object, base);
2025
+ }
2026
+
2027
+ /**
2028
+ * Provide insights into why a setState() call occurred by diffing the before and after values.
2029
+ *
2030
+ * @param {Object} mapping
2031
+ * @param {*} previousValue
2032
+ * @param {*} newValue
2033
+ * @param {String} caller
2034
+ * @param {String} [keyThatChanged]
2035
+ */
2036
+ function logSetStateCall(mapping, previousValue, newValue, caller, keyThatChanged) {
2037
+ if (!debugSetState) {
2038
+ return;
2039
+ }
2040
+
2041
+ var logParams = {};
2042
+ if (keyThatChanged) {
2043
+ logParams.keyThatChanged = keyThatChanged;
2044
+ }
2045
+ if (_underscore.default.isObject(newValue) && _underscore.default.isObject(previousValue)) {
2046
+ logParams.difference = diffObject(previousValue, newValue);
2047
+ } else {
2048
+ logParams.previousValue = previousValue;
2049
+ logParams.newValue = newValue;
2050
+ }
2051
+
2052
+ console.debug("[Onyx-Debug] " + mapping.displayName + " setState() called. Subscribed to key '" + mapping.key + "' (" + caller + ")", logParams);
2053
+ }
2054
+
2055
+ /***/ }),
2056
+
1957
2057
  /***/ "./lib/metrics/index.web.js":
1958
2058
  /*!**********************************!*\
1959
2059
  !*** ./lib/metrics/index.web.js ***!
@@ -2202,8 +2302,8 @@ function _default(mapOnyxToState) {var _this = this;
2202
2302
  omit(function (config) {return config.initWithStoredValues === false;}).
2203
2303
  keys().
2204
2304
  value();
2205
-
2206
- return function (WrappedComponent) {var
2305
+ return function (WrappedComponent) {
2306
+ var displayName = getDisplayName(WrappedComponent);var
2207
2307
  withOnyx = /*#__PURE__*/function (_React$Component) {(0, _inherits2.default)(withOnyx, _React$Component);var _super = _createSuper(withOnyx);
2208
2308
  function withOnyx(props) {var _this2;(0, _classCallCheck2.default)(this, withOnyx);
2209
2309
  _this2 = _super.call(this, props);
@@ -2328,7 +2428,8 @@ function _default(mapOnyxToState) {var _this = this;
2328
2428
  mapping, {
2329
2429
  key: key,
2330
2430
  statePropertyName: statePropertyName,
2331
- withOnyxInstance: this }));
2431
+ withOnyxInstance: this,
2432
+ displayName: displayName }));
2332
2433
 
2333
2434
  } }, { key: "render", value: function render()
2334
2435
 
@@ -2352,7 +2453,7 @@ function _default(mapOnyxToState) {var _this = this;
2352
2453
  , (0, _extends2.default)({}, propsToPass,
2353
2454
 
2354
2455
  stateToPass, {
2355
- ref: this.props.forwardedRef, __self: this, __source: { fileName: _jsxFileName, lineNumber: 173, columnNumber: 21 } })));
2456
+ ref: this.props.forwardedRef, __self: this, __source: { fileName: _jsxFileName, lineNumber: 174, columnNumber: 21 } })));
2356
2457
 
2357
2458
 
2358
2459
  } }]);return withOnyx;}(_react.default.Component);
@@ -2367,11 +2468,11 @@ function _default(mapOnyxToState) {var _this = this;
2367
2468
  withOnyx.defaultProps = {
2368
2469
  forwardedRef: undefined };
2369
2470
 
2370
- withOnyx.displayName = "withOnyx(" + getDisplayName(WrappedComponent) + ")";
2471
+ withOnyx.displayName = "withOnyx(" + displayName + ")";
2371
2472
  return _react.default.forwardRef(function (props, ref) {
2372
2473
  var Component = withOnyx;
2373
2474
  // eslint-disable-next-line react/jsx-props-no-spreading
2374
- return /*#__PURE__*/_react.default.createElement(Component, (0, _extends2.default)({}, props, { forwardedRef: ref, __self: _this, __source: { fileName: _jsxFileName, lineNumber: 197, columnNumber: 20 } }));
2475
+ return /*#__PURE__*/_react.default.createElement(Component, (0, _extends2.default)({}, props, { forwardedRef: ref, __self: _this, __source: { fileName: _jsxFileName, lineNumber: 198, columnNumber: 20 } }));
2375
2476
  });
2376
2477
  };
2377
2478
  }
@@ -3483,6 +3584,17 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_lodash_get__;
3483
3584
 
3484
3585
  /***/ }),
3485
3586
 
3587
+ /***/ "lodash/transform":
3588
+ /*!***********************************!*\
3589
+ !*** external "lodash/transform" ***!
3590
+ \***********************************/
3591
+ /***/ ((module) => {
3592
+
3593
+ "use strict";
3594
+ module.exports = __WEBPACK_EXTERNAL_MODULE_lodash_transform__;
3595
+
3596
+ /***/ }),
3597
+
3486
3598
  /***/ "react":
3487
3599
  /*!************************!*\
3488
3600
  !*** external "react" ***!