react-native-onyx 1.0.17 → 1.0.18

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 CHANGED
@@ -5,6 +5,8 @@
5
5
  ## Functions
6
6
 
7
7
  <dl>
8
+ <dt><a href="#isCollectionMemberKey">isCollectionMemberKey(collectionKey, key)</a> ⇒ <code>Boolean</code></dt>
9
+ <dd></dd>
8
10
  <dt><a href="#connect">connect(mapping)</a> ⇒ <code>Number</code></dt>
9
11
  <dd><p>Subscribes a react component&#39;s state directly to a store key</p>
10
12
  </dd>
@@ -60,6 +62,16 @@ value will be saved to storage after the default value.</p>
60
62
  </dd>
61
63
  </dl>
62
64
 
65
+ <a name="isCollectionMemberKey"></a>
66
+
67
+ ## isCollectionMemberKey(collectionKey, key) ⇒ <code>Boolean</code>
68
+ **Kind**: global function
69
+
70
+ | Param | Type |
71
+ | --- | --- |
72
+ | collectionKey | <code>String</code> |
73
+ | key | <code>String</code> |
74
+
63
75
  <a name="connect"></a>
64
76
 
65
77
  ## connect(mapping) ⇒ <code>Number</code>
@@ -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/merge"), require("lodash/get"), require("localforage"), require("lodash/mergeWith"), require("react"));
3
+ module.exports = factory(require("underscore"), require("expensify-common/lib/str"), require("lodash/get"), require("localforage"), require("lodash/mergeWith"), require("react"));
4
4
  else if(typeof define === 'function' && define.amd)
5
- define(["underscore", "expensify-common/lib/str", "lodash/merge", "lodash/get", "localforage", "lodash/mergeWith", "react"], factory);
5
+ define(["underscore", "expensify-common/lib/str", "lodash/get", "localforage", "lodash/mergeWith", "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/merge"), require("lodash/get"), require("localforage"), require("lodash/mergeWith"), require("react"));
7
+ exports["react-native-onyx/web"] = factory(require("underscore"), require("expensify-common/lib/str"), require("lodash/get"), require("localforage"), require("lodash/mergeWith"), require("react"));
8
8
  else
9
- root["react-native-onyx/web"] = factory(root["underscore"], root["expensify-common/lib/str"], root["lodash/merge"], root["lodash/get"], root["localforage"], root["lodash/mergeWith"], root["react"]);
10
- })(self, (__WEBPACK_EXTERNAL_MODULE_underscore__, __WEBPACK_EXTERNAL_MODULE_expensify_common_lib_str__, __WEBPACK_EXTERNAL_MODULE_lodash_merge__, __WEBPACK_EXTERNAL_MODULE_lodash_get__, __WEBPACK_EXTERNAL_MODULE_localforage__, __WEBPACK_EXTERNAL_MODULE_lodash_mergeWith__, __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/mergeWith"], 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_mergeWith__, __WEBPACK_EXTERNAL_MODULE_react__) => {
11
11
  return /******/ (() => { // webpackBootstrap
12
12
  /******/ var __webpack_modules__ = ({
13
13
 
@@ -489,15 +489,15 @@ function logInfo(message) {
489
489
  \*********************/
490
490
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
491
491
 
492
- var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "./node_modules/@babel/runtime/helpers/interopRequireDefault.js");Object.defineProperty(exports, "__esModule", ({ value: true }));exports["default"] = void 0;var _slicedToArray2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/slicedToArray */ "./node_modules/@babel/runtime/helpers/slicedToArray.js"));var _toConsumableArray2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/toConsumableArray */ "./node_modules/@babel/runtime/helpers/toConsumableArray.js"));var _extends2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/extends */ "./node_modules/@babel/runtime/helpers/extends.js"));var _defineProperty2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/defineProperty */ "./node_modules/@babel/runtime/helpers/defineProperty.js"));var _underscore = _interopRequireDefault(__webpack_require__(/*! underscore */ "underscore"));
492
+ var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "./node_modules/@babel/runtime/helpers/interopRequireDefault.js");Object.defineProperty(exports, "__esModule", ({ value: true }));exports["default"] = void 0;var _slicedToArray2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/slicedToArray */ "./node_modules/@babel/runtime/helpers/slicedToArray.js"));var _toConsumableArray2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/toConsumableArray */ "./node_modules/@babel/runtime/helpers/toConsumableArray.js"));var _extends3 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/extends */ "./node_modules/@babel/runtime/helpers/extends.js"));var _defineProperty2 = _interopRequireDefault(__webpack_require__(/*! @babel/runtime/helpers/defineProperty */ "./node_modules/@babel/runtime/helpers/defineProperty.js"));
493
+ var _underscore = _interopRequireDefault(__webpack_require__(/*! underscore */ "underscore"));
493
494
  var _str = _interopRequireDefault(__webpack_require__(/*! expensify-common/lib/str */ "expensify-common/lib/str"));
494
- var _merge = _interopRequireDefault(__webpack_require__(/*! lodash/merge */ "lodash/merge"));
495
495
  var _get = _interopRequireDefault(__webpack_require__(/*! lodash/get */ "lodash/get"));
496
496
  var _storage = _interopRequireDefault(__webpack_require__(/*! ./storage */ "./lib/storage/index.web.js"));
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 _mergeWithCustomized = _interopRequireDefault(__webpack_require__(/*! ./mergeWithCustomized */ "./lib/mergeWithCustomized.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;}
500
+ var _mergeWithCustomized = _interopRequireDefault(__webpack_require__(/*! ./mergeWithCustomized */ "./lib/mergeWithCustomized.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
501
 
502
502
  // Keeps track of the last connectionID that was used so we can keep incrementing it
503
503
  var lastConnectionID = 0;
@@ -598,8 +598,17 @@ function isCollectionKey(key) {
598
598
  }
599
599
 
600
600
  /**
601
- * Checks to see if a given key matches with the
602
- * configured key of our connected subscriber
601
+ * @param {String} collectionKey
602
+ * @param {String} key
603
+ * @returns {Boolean}
604
+ */
605
+ function isCollectionMemberKey(collectionKey, key) {
606
+ return _str.default.startsWith(key, collectionKey) && key.length > collectionKey.length;
607
+ }
608
+
609
+ /**
610
+ * Checks to see if a provided key is the exact configured key of our connected subscriber
611
+ * or if the provided key is a collection member key (in case our configured key is a "collection key")
603
612
  *
604
613
  * @private
605
614
  * @param {String} configKey
@@ -716,7 +725,7 @@ function addAllSafeEvictionKeysToRecentlyAccessedList() {
716
725
  */
717
726
  function getCachedCollection(collectionKey) {
718
727
  var collectionMemberKeys = _underscore.default.filter(_OnyxCache.default.getAllKeys(),
719
- function (storedKey) {return isKeyMatch(collectionKey, storedKey);});
728
+ function (storedKey) {return isCollectionMemberKey(collectionKey, storedKey);});
720
729
 
721
730
 
722
731
  return _underscore.default.reduce(collectionMemberKeys, function (prev, curr) {
@@ -736,73 +745,103 @@ function getCachedCollection(collectionKey) {
736
745
  *
737
746
  * @private
738
747
  * @param {String} collectionKey
739
- * @param {Object} collection
748
+ * @param {Object} partialCollection - a partial collection of grouped member keys
740
749
  */
741
- function keysChanged(collectionKey, collection) {
742
- // Find all subscribers that were added with connect() and trigger the callback or setState() with the new data
743
- _underscore.default.each(callbackToStateMapping, function (subscriber) {
750
+ function keysChanged(collectionKey, partialCollection) {
751
+ // We are iterating over all subscribers similar to keyChanged(). However, we are looking for subscribers who are subscribing to either a collection key or
752
+ // individual collection key member for the collection that is being updated. It is important to note that the collection parameter cane be a PARTIAL collection
753
+ // and does not represent all of the combined keys and values for a collection key. It is just the "new" data that was merged in via mergeCollection().
754
+ var stateMappingKeys = _underscore.default.keys(callbackToStateMapping);var _loop = function _loop(
755
+ i) {
756
+ var subscriber = callbackToStateMapping[stateMappingKeys[i]];
744
757
  if (!subscriber) {
745
- return;
758
+ return "continue";
759
+ }
760
+
761
+ // Skip iteration if we do not have a collection key or a collection member key on this subscriber
762
+ if (!_str.default.startsWith(subscriber.key, collectionKey)) {
763
+ return "continue";
746
764
  }
747
765
 
748
766
  /**
749
767
  * e.g. Onyx.connect({key: ONYXKEYS.COLLECTION.REPORT, callback: ...});
750
768
  */
751
- var isSubscribedToCollectionKey = isKeyMatch(subscriber.key, collectionKey) &&
752
- isCollectionKey(subscriber.key);
769
+ var isSubscribedToCollectionKey = subscriber.key === collectionKey;
753
770
 
754
771
  /**
755
772
  * e.g. Onyx.connect({key: `${ONYXKEYS.COLLECTION.REPORT}{reportID}`, callback: ...});
756
773
  */
757
- var isSubscribedToCollectionMemberKey = subscriber.key.startsWith(collectionKey);
774
+ var isSubscribedToCollectionMemberKey = isCollectionMemberKey(collectionKey, subscriber.key);
758
775
 
759
- if (isSubscribedToCollectionKey) {
760
- if (_underscore.default.isFunction(subscriber.callback)) {
761
- var cachedCollection = getCachedCollection(collectionKey);
776
+ // We prepare the "cached collection" which is the entire collection + the new partial data that
777
+ // was merged in via mergeCollection().
778
+ var cachedCollection = getCachedCollection(collectionKey);
762
779
 
780
+ // Regular Onyx.connect() subscriber found.
781
+ if (_underscore.default.isFunction(subscriber.callback)) {
782
+ // If they are subscribed to the collection key and using waitForCollectionCallback then we'll
783
+ // send the whole cached collection.
784
+ if (isSubscribedToCollectionKey) {
763
785
  if (subscriber.waitForCollectionCallback) {
764
786
  subscriber.callback(cachedCollection);
765
- return;
787
+ return "continue";
766
788
  }
767
789
 
768
- _underscore.default.each(collection, function (data, dataKey) {
790
+ // If they are not using waitForCollectionCallback then we notify the subscriber with
791
+ // the new merged data but only for any keys in the partial collection.
792
+ var dataKeys = _underscore.default.keys(partialCollection);
793
+ for (var j = 0; j < dataKeys.length; j++) {
794
+ var dataKey = dataKeys[j];
769
795
  subscriber.callback(cachedCollection[dataKey], dataKey);
770
- });
771
- } else if (subscriber.withOnyxInstance) {
796
+ }
797
+ return "continue";
798
+ }
799
+
800
+ // And if the subscriber is specifically only tracking a particular collection member key then we will
801
+ // notify them with the cached data for that key only.
802
+ if (isSubscribedToCollectionMemberKey) {
803
+ subscriber.callback(cachedCollection[subscriber.key], subscriber.key);
804
+ return "continue";
805
+ }
806
+
807
+ return "continue";
808
+ }
809
+
810
+ // React component subscriber found.
811
+ if (subscriber.withOnyxInstance) {
812
+ // We are subscribed to a collection key so we must update the data in state with the new
813
+ // collection member key values from the partial update.
814
+ if (isSubscribedToCollectionKey) {
772
815
  subscriber.withOnyxInstance.setState(function (prevState) {
773
816
  var finalCollection = _underscore.default.clone(prevState[subscriber.statePropertyName] || {});
774
- _underscore.default.each(collection, function (data, dataKey) {
775
- if (finalCollection[dataKey]) {
776
- (0, _merge.default)(finalCollection[dataKey], data);
777
- } else {
778
- finalCollection[dataKey] = data;
779
- }
780
- });
781
817
 
818
+ var dataKeys = _underscore.default.keys(partialCollection);
819
+ for (var _j = 0; _j < dataKeys.length; _j++) {
820
+ var _dataKey = dataKeys[_j];
821
+ finalCollection[_dataKey] = cachedCollection[_dataKey];
822
+ }
782
823
  return (0, _defineProperty2.default)({},
783
824
  subscriber.statePropertyName, finalCollection);
784
825
 
785
826
  });
827
+ return "continue";
786
828
  }
787
- } else if (isSubscribedToCollectionMemberKey) {
788
- var dataFromCollection = collection[subscriber.key];
789
829
 
790
- // If `dataFromCollection` happens to not exist, then return early so that there are no unnecessary
791
- // re-renderings of the component
792
- if (_underscore.default.isUndefined(dataFromCollection)) {
793
- return;
794
- }
795
-
796
- subscriber.withOnyxInstance.setState(function (prevState) {return (0, _defineProperty2.default)({},
797
- subscriber.statePropertyName, _underscore.default.isObject(dataFromCollection) ? (0, _extends2.default)({},
798
-
799
- prevState[subscriber.statePropertyName],
800
- dataFromCollection) :
830
+ // If a React component is only interested in a single key then we can set the cached value directly to the state name.
831
+ if (isSubscribedToCollectionMemberKey) {
832
+ // However, we only want to update this subscriber if the partial data contains a change.
833
+ // Otherwise, we would update them with a value they already have and trigger an unnecessary re-render.
834
+ var dataFromCollection = partialCollection[subscriber.key];
835
+ if (_underscore.default.isUndefined(dataFromCollection)) {
836
+ return "continue";
837
+ }
801
838
 
802
- dataFromCollection);});
839
+ subscriber.withOnyxInstance.setState((0, _defineProperty2.default)({},
840
+ subscriber.statePropertyName, cachedCollection[subscriber.key]));
803
841
 
804
- }
805
- });
842
+ }
843
+ }};for (var i = stateMappingKeys.length; i--;) {var _ret = _loop(i);if (_ret === "continue") continue;
844
+ }
806
845
  }
807
846
 
808
847
  /**
@@ -820,43 +859,54 @@ function keyChanged(key, data) {
820
859
  removeLastAccessedKey(key);
821
860
  }
822
861
 
823
- // Find all subscribers that were added with connect() and trigger the callback or setState() with the new data
824
- _underscore.default.each(callbackToStateMapping, function (subscriber) {
862
+ // We are iterating over all subscribers to see if they are interested in the key that has just changed. If the subscriber's key is a collection key then we will
863
+ // notify them if the key that changed is a collection member. Or if it is a regular key notify them when there is an exact match. Depending on whether the subscriber
864
+ // was connected via withOnyx we will call setState() directly on the withOnyx instance. If it is a regular connection we will pass the data to the provided callback.
865
+ var stateMappingKeys = _underscore.default.keys(callbackToStateMapping);var _loop2 = function _loop2(
866
+ i) {
867
+ var subscriber = callbackToStateMapping[stateMappingKeys[i]];
825
868
  if (!subscriber || !isKeyMatch(subscriber.key, key)) {
826
- return;
869
+ return "continue";
827
870
  }
828
871
 
872
+ // Subscriber is a regular call to connect() and provided a callback
829
873
  if (_underscore.default.isFunction(subscriber.callback)) {
830
- if (subscriber.waitForCollectionCallback) {
874
+ if (isCollectionKey(subscriber.key) && subscriber.waitForCollectionCallback) {
831
875
  var cachedCollection = getCachedCollection(subscriber.key);
832
876
  cachedCollection[key] = data;
833
877
  subscriber.callback(cachedCollection);
834
- return;
878
+ return "continue";
835
879
  }
836
880
 
837
881
  subscriber.callback(data, key);
838
- return;
882
+ return "continue";
839
883
  }
840
884
 
841
- if (!subscriber.withOnyxInstance) {
842
- return;
843
- }
885
+ // Subscriber connected via withOnyx() HOC
886
+ if (subscriber.withOnyxInstance) {
887
+ // Check if we are subscribing to a collection key and overwrite the collection member key value in state
888
+ if (isCollectionKey(subscriber.key)) {
889
+ subscriber.withOnyxInstance.setState(function (prevState) {
890
+ var collection = prevState[subscriber.statePropertyName] || {};
891
+ return (0, _defineProperty2.default)({},
892
+ subscriber.statePropertyName, (0, _extends3.default)({},
893
+ collection, (0, _defineProperty2.default)({},
894
+ key, data)));
844
895
 
845
- // Check if we are subscribing to a collection key and add this item as a collection
846
- if (isCollectionKey(subscriber.key)) {
847
- subscriber.withOnyxInstance.setState(function (prevState) {
848
- var collection = _underscore.default.clone(prevState[subscriber.statePropertyName] || {});
849
- collection[key] = data;
850
- return (0, _defineProperty2.default)({},
851
- subscriber.statePropertyName, collection);
852
896
 
853
- });
854
- } else {
897
+ });
898
+ return "continue";
899
+ }
900
+
901
+ // If we did not match on a collection key then we just set the new data to the state property
855
902
  subscriber.withOnyxInstance.setState((0, _defineProperty2.default)({},
856
903
  subscriber.statePropertyName, data));
857
904
 
905
+ return "continue";
858
906
  }
859
- });
907
+
908
+ console.error('Warning: Found a matching subscriber to a key that changed, but no callback or withOnyxInstance could be found.');};for (var i = stateMappingKeys.length; i--;) {var _ret2 = _loop2(i);if (_ret2 === "continue") continue;
909
+ }
860
910
  }
861
911
 
862
912
  /**
@@ -870,9 +920,9 @@ function keyChanged(key, data) {
870
920
  * @param {string} [config.statePropertyName]
871
921
  * @param {function} [config.callback]
872
922
  * @param {*|null} val
873
- * @param {String} key
923
+ * @param {String} matchedKey
874
924
  */
875
- function sendDataToConnection(config, val, key) {
925
+ function sendDataToConnection(config, val, matchedKey) {
876
926
  // If the mapping no longer exists then we should not send any data.
877
927
  // This means our subscriber disconnected or withOnyx wrapped component unmounted.
878
928
  if (!callbackToStateMapping[config.connectionID]) {
@@ -882,10 +932,54 @@ function sendDataToConnection(config, val, key) {
882
932
  if (config.withOnyxInstance) {
883
933
  config.withOnyxInstance.setWithOnyxState(config.statePropertyName, val);
884
934
  } else if (_underscore.default.isFunction(config.callback)) {
885
- config.callback(val, key);
935
+ config.callback(val, matchedKey);
886
936
  }
887
937
  }
888
938
 
939
+ /**
940
+ * We check to see if this key is flagged as safe for eviction and add it to the recentlyAccessedKeys list so that when we
941
+ * run out of storage the least recently accessed key can be removed.
942
+ *
943
+ * @private
944
+ * @param {Object} mapping
945
+ */
946
+ function addKeyToRecentlyAccessedIfNeeded(mapping) {
947
+ if (!isSafeEvictionKey(mapping.key)) {
948
+ return;
949
+ }
950
+
951
+ // Try to free some cache whenever we connect to a safe eviction key
952
+ _OnyxCache.default.removeLeastRecentlyUsedKeys();
953
+
954
+ if (mapping.withOnyxInstance && !isCollectionKey(mapping.key)) {
955
+ // All React components subscribing to a key flagged as a safe eviction key must implement the canEvict property.
956
+ if (_underscore.default.isUndefined(mapping.canEvict)) {
957
+ throw new Error("Cannot subscribe to safe eviction key '" +
958
+ mapping.key + "' without providing a canEvict value.");
959
+
960
+ }
961
+
962
+ addLastAccessedKey(mapping.key);
963
+ }
964
+ }
965
+
966
+ /**
967
+ * Gets the data for a given an array of matching keys, combines them into an object, and sends the result back to the subscriber.
968
+ *
969
+ * @private
970
+ * @param {Array} matchingKeys
971
+ * @param {Object} mapping
972
+ */
973
+ function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
974
+ Promise.all(_underscore.default.map(matchingKeys, function (key) {return get(key);})).
975
+ then(function (values) {return _underscore.default.reduce(values, function (finalObject, value, i) {
976
+ // eslint-disable-next-line no-param-reassign
977
+ finalObject[matchingKeys[i]] = value;
978
+ return finalObject;
979
+ }, {});}).
980
+ then(function (val) {return sendDataToConnection(mapping, val);});
981
+ }
982
+
889
983
  /**
890
984
  * Subscribes a react component's state directly to a store key
891
985
  *
@@ -918,58 +1012,63 @@ function connect(mapping) {
918
1012
 
919
1013
  // Commit connection only after init passes
920
1014
  deferredInitTask.promise.
921
- then(function () {
922
- // Check to see if this key is flagged as a safe eviction key and add it to the recentlyAccessedKeys list
923
- if (!isSafeEvictionKey(mapping.key)) {
1015
+ then(function () {return addKeyToRecentlyAccessedIfNeeded(mapping);}).
1016
+ then(getAllKeys).
1017
+ then(function (keys) {
1018
+ // We search all the keys in storage to see if any are a "match" for the subscriber we are connecting so that we
1019
+ // can send data back to the subscriber. Note that multiple keys can match as a subscriber could either be
1020
+ // subscribed to a "collection key" or a single key.
1021
+ var matchingKeys = _underscore.default.filter(keys, function (key) {return isKeyMatch(mapping.key, key);});
1022
+
1023
+ // If the key being connected to does not exist we initialize the value with null. For subscribers that connected
1024
+ // directly via connect() they will simply get a null value sent to them without any information about which key matched
1025
+ // since there are none matched. In withOnyx() we wait for all connected keys to return a value before rendering the child
1026
+ // component. This null value will be filtered out so that the connected component can utilize defaultProps.
1027
+ if (matchingKeys.length === 0) {
1028
+ sendDataToConnection(mapping, null);
924
1029
  return;
925
1030
  }
926
1031
 
927
- // Try to free some cache whenever we connect to a safe eviction key
928
- _OnyxCache.default.removeLeastRecentlyUsedKeys();
929
-
930
- if (mapping.withOnyxInstance && !isCollectionKey(mapping.key)) {
931
- // All React components subscribing to a key flagged as a safe eviction
932
- // key must implement the canEvict property.
933
- if (_underscore.default.isUndefined(mapping.canEvict)) {
934
- throw new Error("Cannot subscribe to safe eviction key '" +
935
- mapping.key + "' without providing a canEvict value.");
1032
+ // When using a callback subscriber we will either trigger the provided callback for each key we find or combine all values
1033
+ // into an object and just make a single call. The latter behavior is enabled by providing a waitForCollectionCallback key
1034
+ // combined with a subscription to a collection key.
1035
+ if (_underscore.default.isFunction(mapping.callback)) {
1036
+ if (isCollectionKey(mapping.key)) {
1037
+ if (mapping.waitForCollectionCallback) {
1038
+ getCollectionDataAndSendAsObject(matchingKeys, mapping);
1039
+ return;
1040
+ }
936
1041
 
1042
+ // We did not opt into using waitForCollectionCallback mode so the callback is called for every matching key.
1043
+ var _loop3 = function _loop3(i) {
1044
+ get(matchingKeys[i]).then(function (val) {return sendDataToConnection(mapping, val, matchingKeys[i]);});};for (var i = 0; i < matchingKeys.length; i++) {_loop3(i);
1045
+ }
1046
+ return;
937
1047
  }
938
1048
 
939
- addLastAccessedKey(mapping.key);
1049
+ // If we are not subscribed to a collection key then there's only a single key to send an update for.
1050
+ get(mapping.key).then(function (val) {return sendDataToConnection(mapping, val, mapping.key);});
1051
+ return;
940
1052
  }
941
- }).
942
- then(getAllKeys).
943
- then(function (keys) {
944
- // Find all the keys matched by the config key
945
- var matchingKeys = _underscore.default.filter(keys, function (key) {return isKeyMatch(mapping.key, key);});
946
1053
 
947
- // If the key being connected to does not exist, initialize the value with null
948
- if (matchingKeys.length === 0) {
949
- sendDataToConnection(mapping, null);
1054
+ // If we have a withOnyxInstance that means a React component has subscribed via the withOnyx() HOC and we need to
1055
+ // group collection key member data into an object.
1056
+ if (mapping.withOnyxInstance) {
1057
+ if (isCollectionKey(mapping.key)) {
1058
+ getCollectionDataAndSendAsObject(matchingKeys, mapping);
1059
+ return;
1060
+ }
1061
+
1062
+ // If the subscriber is not using a collection key then we just send a single value back to the subscriber
1063
+ get(mapping.key).then(function (val) {return sendDataToConnection(mapping, val, mapping.key);});
950
1064
  return;
951
1065
  }
952
1066
 
953
- // When using a callback subscriber we will trigger the callback
954
- // for each key we find. It's up to the subscriber to know whether
955
- // to expect a single key or multiple keys in the case of a collection.
956
- // React components are an exception since we'll want to send their
957
- // initial data as a single object when using collection keys.
958
- if (mapping.withOnyxInstance && isCollectionKey(mapping.key) || mapping.waitForCollectionCallback) {
959
- Promise.all(_underscore.default.map(matchingKeys, function (key) {return get(key);})).
960
- then(function (values) {return _underscore.default.reduce(values, function (finalObject, value, i) {
961
- // eslint-disable-next-line no-param-reassign
962
- finalObject[matchingKeys[i]] = value;
963
- return finalObject;
964
- }, {});}).
965
- then(function (val) {return sendDataToConnection(mapping, val);});
966
- } else {
967
- _underscore.default.each(matchingKeys, function (key) {
968
- get(key).then(function (val) {return sendDataToConnection(mapping, val, key);});
969
- });
970
- }
1067
+ console.error('Warning: Onyx.connect() was found without a callback or withOnyxInstance');
971
1068
  });
972
1069
 
1070
+ // The connectionID is returned back to the caller so that it can be used to clean up the connection when it's no longer needed
1071
+ // by calling Onyx.disconnect(connectionID).
973
1072
  return connectionID;
974
1073
  }
975
1074
 
@@ -1279,7 +1378,7 @@ function clear() {
1279
1378
  */
1280
1379
  function mergeCollection(collectionKey, collection) {
1281
1380
  // Confirm all the collection keys belong to the same parent
1282
- _underscore.default.each(collection, function (data, dataKey) {
1381
+ _underscore.default.each(collection, function (_data, dataKey) {
1283
1382
  if (isKeyMatch(collectionKey, dataKey)) {
1284
1383
  return;
1285
1384
  }
@@ -1331,7 +1430,7 @@ function mergeCollection(collectionKey, collection) {
1331
1430
  */
1332
1431
  function update(data) {
1333
1432
  // First, validate the Onyx object is in the format we expect
1334
- _underscore.default.each(data, function (_ref4) {var onyxMethod = _ref4.onyxMethod,key = _ref4.key;
1433
+ _underscore.default.each(data, function (_ref3) {var onyxMethod = _ref3.onyxMethod,key = _ref3.key;
1335
1434
  if (!_underscore.default.contains(['clear', 'set', 'merge', 'mergecollection'], onyxMethod)) {
1336
1435
  throw new Error("Invalid onyxMethod " + onyxMethod + " in Onyx update.");
1337
1436
  }
@@ -1340,7 +1439,7 @@ function update(data) {
1340
1439
  }
1341
1440
  });
1342
1441
 
1343
- _underscore.default.each(data, function (_ref5) {var onyxMethod = _ref5.onyxMethod,key = _ref5.key,value = _ref5.value;
1442
+ _underscore.default.each(data, function (_ref4) {var onyxMethod = _ref4.onyxMethod,key = _ref4.key,value = _ref4.value;
1344
1443
  switch (onyxMethod) {
1345
1444
  case 'set':
1346
1445
  set(key, value);
@@ -1394,7 +1493,7 @@ function init()
1394
1493
 
1395
1494
 
1396
1495
 
1397
- {var _ref6 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},_ref6$keys = _ref6.keys,keys = _ref6$keys === void 0 ? {} : _ref6$keys,_ref6$initialKeyState = _ref6.initialKeyStates,initialKeyStates = _ref6$initialKeyState === void 0 ? {} : _ref6$initialKeyState,_ref6$safeEvictionKey = _ref6.safeEvictionKeys,safeEvictionKeys = _ref6$safeEvictionKey === void 0 ? [] : _ref6$safeEvictionKey,_ref6$maxCachedKeysCo = _ref6.maxCachedKeysCount,maxCachedKeysCount = _ref6$maxCachedKeysCo === void 0 ? 1000 : _ref6$maxCachedKeysCo,_ref6$captureMetrics = _ref6.captureMetrics,captureMetrics = _ref6$captureMetrics === void 0 ? false : _ref6$captureMetrics,_ref6$shouldSyncMulti = _ref6.shouldSyncMultipleInstances,shouldSyncMultipleInstances = _ref6$shouldSyncMulti === void 0 ? Boolean(__webpack_require__.g.localStorage) : _ref6$shouldSyncMulti,_ref6$keysToDisableSy = _ref6.keysToDisableSyncEvents,keysToDisableSyncEvents = _ref6$keysToDisableSy === void 0 ? [] : _ref6$keysToDisableSy;
1496
+ {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;
1398
1497
  if (captureMetrics) {
1399
1498
  // The code here is only bundled and applied when the captureMetrics is set
1400
1499
  // eslint-disable-next-line no-use-before-define
@@ -3337,17 +3436,6 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_lodash_get__;
3337
3436
 
3338
3437
  /***/ }),
3339
3438
 
3340
- /***/ "lodash/merge":
3341
- /*!*******************************!*\
3342
- !*** external "lodash/merge" ***!
3343
- \*******************************/
3344
- /***/ ((module) => {
3345
-
3346
- "use strict";
3347
- module.exports = __WEBPACK_EXTERNAL_MODULE_lodash_merge__;
3348
-
3349
- /***/ }),
3350
-
3351
3439
  /***/ "lodash/mergeWith":
3352
3440
  /*!***********************************!*\
3353
3441
  !*** external "lodash/mergeWith" ***!