react-native-onyx 1.0.24 → 1.0.25

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,14 @@
5
5
  ## Functions
6
6
 
7
7
  <dl>
8
+ <dt><a href="#getSubsetOfData">getSubsetOfData(sourceData, selector)</a> ⇒ <code>Mixed</code></dt>
9
+ <dd><p>Uses a selector string or function to return a simplified version of sourceData</p>
10
+ </dd>
11
+ <dt><a href="#reduceCollectionWithSelector">reduceCollectionWithSelector(collection, selector)</a> ⇒ <code>Object</code></dt>
12
+ <dd><p>Takes a collection of items (eg. {testKey_1:{a:&#39;a&#39;}, testKey_2:{b:&#39;b&#39;}})
13
+ and runs it through a reducer function to return a subset of the data according to a selector.
14
+ The resulting collection will only contain items that are returned by the selector.</p>
15
+ </dd>
8
16
  <dt><a href="#isCollectionMemberKey">isCollectionMemberKey(collectionKey, key)</a> ⇒ <code>Boolean</code></dt>
9
17
  <dd></dd>
10
18
  <dt><a href="#connect">connect(mapping)</a> ⇒ <code>Number</code></dt>
@@ -62,23 +70,49 @@ value will be saved to storage after the default value.</p>
62
70
  </dd>
63
71
  </dl>
64
72
 
73
+ <a name="getSubsetOfData"></a>
74
+
75
+ ## getSubsetOfData(sourceData, selector) ⇒ <code>Mixed</code>
76
+ Uses a selector string or function to return a simplified version of sourceData
77
+
78
+ **Kind**: global function
79
+
80
+ | Param | Type | Description |
81
+ | --- | --- | --- |
82
+ | sourceData | <code>Mixed</code> | |
83
+ | selector | <code>String</code> \| <code>function</code> | If it's a string, the selector is passed to lodashGet on the sourceData If it's a function, it is passed the sourceData and it should return the simplified data |
84
+
85
+ <a name="reduceCollectionWithSelector"></a>
86
+
87
+ ## reduceCollectionWithSelector(collection, selector) ⇒ <code>Object</code>
88
+ Takes a collection of items (eg. {testKey_1:{a:'a'}, testKey_2:{b:'b'}})
89
+ and runs it through a reducer function to return a subset of the data according to a selector.
90
+ The resulting collection will only contain items that are returned by the selector.
91
+
92
+ **Kind**: global function
93
+
94
+ | Param | Type | Description |
95
+ | --- | --- | --- |
96
+ | collection | <code>Object</code> | |
97
+ | selector | <code>String</code> \| <code>function</code> | (see method docs for getSubsetOfData() for full details) |
98
+
65
99
  <a name="isCollectionMemberKey"></a>
66
100
 
67
101
  ## isCollectionMemberKey(collectionKey, key) ⇒ <code>Boolean</code>
68
- **Kind**: global function
102
+ **Kind**: global function
69
103
 
70
104
  | Param | Type |
71
105
  | --- | --- |
72
- | collectionKey | <code>String</code> |
73
- | key | <code>String</code> |
106
+ | collectionKey | <code>String</code> |
107
+ | key | <code>String</code> |
74
108
 
75
109
  <a name="connect"></a>
76
110
 
77
111
  ## connect(mapping) ⇒ <code>Number</code>
78
112
  Subscribes a react component's state directly to a store key
79
113
 
80
- **Kind**: global function
81
- **Returns**: <code>Number</code> - an ID to use when calling disconnect
114
+ **Kind**: global function
115
+ **Returns**: <code>Number</code> - an ID to use when calling disconnect
82
116
 
83
117
  | Param | Type | Description |
84
118
  | --- | --- | --- |
@@ -89,8 +123,9 @@ Subscribes a react component's state directly to a store key
89
123
  | [mapping.callback] | <code>function</code> | a method that will be called with changed data This is used by any non-React code to connect to Onyx |
90
124
  | [mapping.initWithStoredValues] | <code>Boolean</code> | If set to false, then no data will be prefilled into the component |
91
125
  | [mapping.waitForCollectionCallback] | <code>Boolean</code> | If set to true, it will return the entire collection to the callback as a single object |
126
+ | [mapping.selector] | <code>String</code> \| <code>function</code> | THIS PARAM IS ONLY USED WITH withOnyx(). If included, this will be used to subscribe to a subset of an Onyx key's data. If the selector is a string, the selector is passed to lodashGet on the sourceData. If the selector is a function, the sourceData is passed to the selector and should return the simplified data. Using this setting on `withOnyx` can have very positive 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 cause the component to re-render (and that can be expensive from a performance standpoint). |
92
127
 
93
- **Example**
128
+ **Example**
94
129
  ```js
95
130
  const connectionID = Onyx.connect({
96
131
  key: ONYXKEYS.SESSION,
@@ -102,14 +137,14 @@ const connectionID = Onyx.connect({
102
137
  ## disconnect(connectionID, [keyToRemoveFromEvictionBlocklist])
103
138
  Remove the listener for a react component
104
139
 
105
- **Kind**: global function
140
+ **Kind**: global function
106
141
 
107
142
  | Param | Type | Description |
108
143
  | --- | --- | --- |
109
144
  | connectionID | <code>Number</code> | unique id returned by call to Onyx.connect() |
110
145
  | [keyToRemoveFromEvictionBlocklist] | <code>String</code> | |
111
146
 
112
- **Example**
147
+ **Example**
113
148
  ```js
114
149
  Onyx.disconnect(connectionID);
115
150
  ```
@@ -121,7 +156,7 @@ For this reason, Onyx works more similar to what you might expect from a native
121
156
  available async. Since we have code in our main applications that might expect things to work this way it's not safe to change this
122
157
  behavior just yet.
123
158
 
124
- **Kind**: global function
159
+ **Kind**: global function
125
160
 
126
161
  | Param | Type | Description |
127
162
  | --- | --- | --- |
@@ -129,7 +164,7 @@ behavior just yet.
129
164
  | value | <code>\*</code> | |
130
165
  | [canUpdateSubscriber] | <code>function</code> | only subscribers that pass this truth test will be updated |
131
166
 
132
- **Example**
167
+ **Example**
133
168
  ```js
134
169
  notifySubscribersOnNextTick(key, value, subscriber => subscriber.initWithStoredValues === false)
135
170
  ```
@@ -138,7 +173,7 @@ notifySubscribersOnNextTick(key, value, subscriber => subscriber.initWithStoredV
138
173
  ## set(key, value) ⇒ <code>Promise</code>
139
174
  Write a value to our store with the given key
140
175
 
141
- **Kind**: global function
176
+ **Kind**: global function
142
177
 
143
178
  | Param | Type | Description |
144
179
  | --- | --- | --- |
@@ -150,13 +185,13 @@ Write a value to our store with the given key
150
185
  ## multiSet(data) ⇒ <code>Promise</code>
151
186
  Sets multiple keys and values
152
187
 
153
- **Kind**: global function
188
+ **Kind**: global function
154
189
 
155
190
  | Param | Type | Description |
156
191
  | --- | --- | --- |
157
192
  | data | <code>Object</code> | object keyed by ONYXKEYS and the values to set |
158
193
 
159
- **Example**
194
+ **Example**
160
195
  ```js
161
196
  Onyx.multiSet({'key1': 'a', 'key2': 'b'});
162
197
  ```
@@ -174,14 +209,14 @@ Calls to `Onyx.merge()` are batched so that any calls performed in a single tick
174
209
  applied in the order they were called. Note: `Onyx.set()` calls do not work this way so use caution when mixing
175
210
  `Onyx.merge()` and `Onyx.set()`.
176
211
 
177
- **Kind**: global function
212
+ **Kind**: global function
178
213
 
179
214
  | Param | Type | Description |
180
215
  | --- | --- | --- |
181
216
  | key | <code>String</code> | ONYXKEYS key |
182
217
  | value | <code>Object</code> \| <code>Array</code> | Object or Array value to merge |
183
218
 
184
- **Example**
219
+ **Example**
185
220
  ```js
186
221
  Onyx.merge(ONYXKEYS.EMPLOYEE_LIST, ['Joe']); // -> ['Joe']
187
222
  Onyx.merge(ONYXKEYS.EMPLOYEE_LIST, ['Jack']); // -> ['Joe', 'Jack']
@@ -209,20 +244,20 @@ Onyx.get(key) before calling Storage.setItem() via Onyx.set().
209
244
  Storage.setItem() from Onyx.clear() will have already finished and the merged
210
245
  value will be saved to storage after the default value.
211
246
 
212
- **Kind**: global function
247
+ **Kind**: global function
213
248
  <a name="mergeCollection"></a>
214
249
 
215
250
  ## mergeCollection(collectionKey, collection) ⇒ <code>Promise</code>
216
251
  Merges a collection based on their keys
217
252
 
218
- **Kind**: global function
253
+ **Kind**: global function
219
254
 
220
255
  | Param | Type | Description |
221
256
  | --- | --- | --- |
222
257
  | collectionKey | <code>String</code> | e.g. `ONYXKEYS.COLLECTION.REPORT` |
223
258
  | collection | <code>Object</code> | Object collection keyed by individual collection member keys and values |
224
259
 
225
- **Example**
260
+ **Example**
226
261
  ```js
227
262
  Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, {
228
263
  [`${ONYXKEYS.COLLECTION.REPORT}1`]: report1,
@@ -234,7 +269,7 @@ Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, {
234
269
  ## update(data)
235
270
  Insert API responses and lifecycle data into Onyx
236
271
 
237
- **Kind**: global function
272
+ **Kind**: global function
238
273
 
239
274
  | Param | Type | Description |
240
275
  | --- | --- | --- |
@@ -245,7 +280,7 @@ Insert API responses and lifecycle data into Onyx
245
280
  ## init([options])
246
281
  Initialize the store with actions and listening for storage events
247
282
 
248
- **Kind**: global function
283
+ **Kind**: global function
249
284
 
250
285
  | Param | Type | Default | Description |
251
286
  | --- | --- | --- | --- |
@@ -258,7 +293,7 @@ Initialize the store with actions and listening for storage events
258
293
  | [options.shouldSyncMultipleInstances] | <code>Boolean</code> | | Auto synchronize storage events between multiple instances of Onyx running in different tabs/windows. Defaults to true for platforms that support local storage (web/desktop) |
259
294
  | [options.debugSetState] | <code>Boolean</code> | | Enables debugging setState() calls to connected components. |
260
295
 
261
- **Example**
296
+ **Example**
262
297
  ```js
263
298
  Onyx.init({
264
299
  keys: ONYXKEYS,
@@ -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("fast-equals"), require("lodash/transform"), require("react"));
3
+ module.exports = factory(require("underscore"), require("expensify-common/lib/str"), require("fast-equals"), 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", "fast-equals", "lodash/transform", "react"], factory);
5
+ define(["underscore", "expensify-common/lib/str", "fast-equals", "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("fast-equals"), require("lodash/transform"), require("react"));
7
+ exports["react-native-onyx/web"] = factory(require("underscore"), require("expensify-common/lib/str"), require("fast-equals"), 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["fast-equals"], 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_fast_equals__, __WEBPACK_EXTERNAL_MODULE_lodash_transform__, __WEBPACK_EXTERNAL_MODULE_react__) => {
9
+ root["react-native-onyx/web"] = factory(root["underscore"], root["expensify-common/lib/str"], root["fast-equals"], 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_fast_equals__, __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
 
@@ -489,9 +489,10 @@ 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 _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"));
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 _extends4 = _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
493
  var _underscore = _interopRequireDefault(__webpack_require__(/*! underscore */ "underscore"));
494
494
  var _str = _interopRequireDefault(__webpack_require__(/*! expensify-common/lib/str */ "expensify-common/lib/str"));
495
+ var _fastEquals = __webpack_require__(/*! fast-equals */ "fast-equals");
495
496
  var _get = _interopRequireDefault(__webpack_require__(/*! lodash/get */ "lodash/get"));
496
497
  var _storage = _interopRequireDefault(__webpack_require__(/*! ./storage */ "./lib/storage/index.web.js"));
497
498
  var Logger = _interopRequireWildcard(__webpack_require__(/*! ./Logger */ "./lib/Logger.js"));
@@ -526,6 +527,33 @@ var defaultKeyStates = {};
526
527
  // Connections can be made before `Onyx.init`. They would wait for this task before resolving
527
528
  var deferredInitTask = (0, _createDeferredTask.default)();
528
529
 
530
+ /**
531
+ * Uses a selector string or function to return a simplified version of sourceData
532
+ * @param {Mixed} sourceData
533
+ * @param {String|Function} selector
534
+ * If it's a string, the selector is passed to lodashGet on the sourceData
535
+ * If it's a function, it is passed the sourceData and it should return the simplified data
536
+ * @returns {Mixed}
537
+ */
538
+ var getSubsetOfData = function getSubsetOfData(sourceData, selector) {return _underscore.default.isFunction(selector) ?
539
+ selector(sourceData) :
540
+ (0, _get.default)(sourceData, selector);};
541
+
542
+ /**
543
+ * Takes a collection of items (eg. {testKey_1:{a:'a'}, testKey_2:{b:'b'}})
544
+ * and runs it through a reducer function to return a subset of the data according to a selector.
545
+ * The resulting collection will only contain items that are returned by the selector.
546
+ * @param {Object} collection
547
+ * @param {String|Function} selector (see method docs for getSubsetOfData() for full details)
548
+ * @returns {Object}
549
+ */
550
+ var reduceCollectionWithSelector = function reduceCollectionWithSelector(collection, selector) {return _underscore.default.reduce(collection, function (finalCollection, item, key) {
551
+ // eslint-disable-next-line no-param-reassign
552
+ finalCollection[key] = getSubsetOfData(item, selector);
553
+
554
+ return finalCollection;
555
+ }, {});};
556
+
529
557
  /**
530
558
  * Get some data from the store
531
559
  *
@@ -813,6 +841,23 @@ function keysChanged(collectionKey, partialCollection) {
813
841
  // We are subscribed to a collection key so we must update the data in state with the new
814
842
  // collection member key values from the partial update.
815
843
  if (isSubscribedToCollectionKey) {
844
+ // If the subscriber has a selector, then the component's state must only be updated with the data
845
+ // returned by the selector.
846
+ if (subscriber.selector) {
847
+ subscriber.withOnyxInstance.setState(function (prevState) {
848
+ var previousData = reduceCollectionWithSelector(prevState[subscriber.statePropertyName], subscriber.selector);
849
+ var newData = reduceCollectionWithSelector(cachedCollection, subscriber.selector);
850
+
851
+ if (!(0, _fastEquals.deepEqual)(previousData, newData)) {
852
+ return (0, _defineProperty2.default)({},
853
+ subscriber.statePropertyName, newData);
854
+
855
+ }
856
+ return null;
857
+ });
858
+ return "continue";
859
+ }
860
+
816
861
  subscriber.withOnyxInstance.setState(function (prevState) {
817
862
  var finalCollection = _underscore.default.clone(prevState[subscriber.statePropertyName] || {});
818
863
  var dataKeys = _underscore.default.keys(partialCollection);
@@ -838,6 +883,25 @@ function keysChanged(collectionKey, partialCollection) {
838
883
  return "continue";
839
884
  }
840
885
 
886
+ // If the subscriber has a selector, then the component's state must only be updated with the data
887
+ // returned by the selector and the state should only change when the subset of data changes from what
888
+ // it was previously.
889
+ if (subscriber.selector) {
890
+ subscriber.withOnyxInstance.setState(function (prevState) {
891
+ var prevData = prevState[subscriber.statePropertyName];
892
+ var newData = getSubsetOfData(cachedCollection[subscriber.key], subscriber.selector);
893
+ if (!(0, _fastEquals.deepEqual)(prevData, newData)) {
894
+ PerformanceUtils.logSetStateCall(subscriber, prevData, newData, 'keysChanged', collectionKey);
895
+ return (0, _defineProperty2.default)({},
896
+ subscriber.statePropertyName, newData);
897
+
898
+ }
899
+
900
+ return null;
901
+ });
902
+ return "continue";
903
+ }
904
+
841
905
  subscriber.withOnyxInstance.setState(function (prevState) {
842
906
  var data = cachedCollection[subscriber.key];
843
907
  var previousData = prevState[subscriber.statePropertyName];
@@ -901,9 +965,32 @@ function keyChanged(key, data, canUpdateSubscriber) {
901
965
  if (subscriber.withOnyxInstance) {
902
966
  // Check if we are subscribing to a collection key and overwrite the collection member key value in state
903
967
  if (isCollectionKey(subscriber.key)) {
968
+ // If the subscriber has a selector, then the consumer of this data must only be given the data
969
+ // returned by the selector and only when the selected data has changed.
970
+ if (subscriber.selector) {
971
+ subscriber.withOnyxInstance.setState(function (prevState) {
972
+ var prevData = prevState[subscriber.statePropertyName];
973
+ var newData = (0, _defineProperty2.default)({},
974
+ key, getSubsetOfData(data, subscriber.selector));
975
+
976
+ var prevDataWithNewData = (0, _extends4.default)({},
977
+ prevData, (0, _defineProperty2.default)({},
978
+ key, getSubsetOfData(data, subscriber.selector)));
979
+
980
+ if (!(0, _fastEquals.deepEqual)(prevData, prevDataWithNewData)) {
981
+ PerformanceUtils.logSetStateCall(subscriber, prevData, newData, 'keyChanged', key);
982
+ return (0, _defineProperty2.default)({},
983
+ subscriber.statePropertyName, prevDataWithNewData);
984
+
985
+ }
986
+ return null;
987
+ });
988
+ return "continue";
989
+ }
990
+
904
991
  subscriber.withOnyxInstance.setState(function (prevState) {
905
992
  var collection = prevState[subscriber.statePropertyName] || {};
906
- var newCollection = (0, _extends3.default)({},
993
+ var newCollection = (0, _extends4.default)({},
907
994
  collection, (0, _defineProperty2.default)({},
908
995
  key, data));
909
996
 
@@ -915,6 +1002,22 @@ function keyChanged(key, data, canUpdateSubscriber) {
915
1002
  return "continue";
916
1003
  }
917
1004
 
1005
+ // If the subscriber has a selector, then the component's state must only be updated with the data
1006
+ // returned by the selector and only if the selected data has changed.
1007
+ if (subscriber.selector) {
1008
+ subscriber.withOnyxInstance.setState(function (prevState) {
1009
+ var previousValue = getSubsetOfData(prevState, subscriber.selector);
1010
+ var newValue = getSubsetOfData(data, subscriber.selector);
1011
+ if (!(0, _fastEquals.deepEqual)(previousValue, newValue)) {
1012
+ return (0, _defineProperty2.default)({},
1013
+ subscriber.statePropertyName, newValue);
1014
+
1015
+ }
1016
+ return null;
1017
+ });
1018
+ return "continue";
1019
+ }
1020
+
918
1021
  // If we did not match on a collection key then we just set the new data to the state property
919
1022
  subscriber.withOnyxInstance.setState(function (prevState) {
920
1023
  var previousData = prevState[subscriber.statePropertyName];
@@ -940,25 +1043,41 @@ function keyChanged(key, data, canUpdateSubscriber) {
940
1043
  * - triggers the callback function
941
1044
  *
942
1045
  * @private
943
- * @param {object} config
944
- * @param {object} [config.withOnyxInstance]
945
- * @param {string} [config.statePropertyName]
946
- * @param {function} [config.callback]
1046
+ * @param {Object} mapping
1047
+ * @param {Object} [mapping.withOnyxInstance]
1048
+ * @param {String} [mapping.statePropertyName]
1049
+ * @param {Function} [mapping.callback]
1050
+ * @param {String} [mapping.selector]
947
1051
  * @param {*|null} val
948
1052
  * @param {String} matchedKey
949
1053
  */
950
- function sendDataToConnection(config, val, matchedKey) {
1054
+ function sendDataToConnection(mapping, val, matchedKey) {
951
1055
  // If the mapping no longer exists then we should not send any data.
952
1056
  // This means our subscriber disconnected or withOnyx wrapped component unmounted.
953
- if (!callbackToStateMapping[config.connectionID]) {
1057
+ if (!callbackToStateMapping[mapping.connectionID]) {
1058
+ return;
1059
+ }
1060
+
1061
+ if (mapping.withOnyxInstance) {
1062
+ var newData = val;
1063
+
1064
+ // If the mapping has a selector, then the component's state must only be updated with the data
1065
+ // returned by the selector.
1066
+ if (mapping.selector) {
1067
+ if (isCollectionKey(mapping.key)) {
1068
+ newData = reduceCollectionWithSelector(val, mapping.selector);
1069
+ } else {
1070
+ newData = getSubsetOfData(val, mapping.selector);
1071
+ }
1072
+ }
1073
+
1074
+ PerformanceUtils.logSetStateCall(mapping, null, newData, 'sendDataToConnection');
1075
+ mapping.withOnyxInstance.setWithOnyxState(mapping.statePropertyName, newData);
954
1076
  return;
955
1077
  }
956
1078
 
957
- if (config.withOnyxInstance) {
958
- PerformanceUtils.logSetStateCall(config, null, val, 'sendDataToConnection');
959
- config.withOnyxInstance.setWithOnyxState(config.statePropertyName, val);
960
- } else if (_underscore.default.isFunction(config.callback)) {
961
- config.callback(val, matchedKey);
1079
+ if (_underscore.default.isFunction(mapping.callback)) {
1080
+ mapping.callback(val, matchedKey);
962
1081
  }
963
1082
  }
964
1083
 
@@ -1025,6 +1144,7 @@ function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
1025
1144
  * @param {Boolean} [mapping.initWithStoredValues] If set to false, then no data will be prefilled into the
1026
1145
  * component
1027
1146
  * @param {Boolean} [mapping.waitForCollectionCallback] If set to true, it will return the entire collection to the callback as a single object
1147
+ * @param {String|Function} [mapping.selector] THIS PARAM IS ONLY USED WITH withOnyx(). If included, this will be used to subscribe to a subset of an Onyx key's data. If the selector is a string, the selector is passed to lodashGet on the sourceData. If the selector is a function, the sourceData is passed to the selector and should return the simplified data. Using this setting on `withOnyx` can have very positive 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 cause the component to re-render (and that can be expensive from a performance standpoint).
1028
1148
  * @returns {Number} an ID to use when calling disconnect
1029
1149
  */
1030
1150
  function connect(mapping) {
@@ -1292,7 +1412,7 @@ function applyMerge(key, data) {
1292
1412
  return _underscore.default.reduce(mergeValues, function (modifiedData, mergeValue) {
1293
1413
  // lodash adds a small overhead so we don't use it here
1294
1414
  // eslint-disable-next-line prefer-object-spread, rulesdir/prefer-underscore-method
1295
- var newData = (0, _extends3.default)({}, (0, _fastMerge.default)(modifiedData, mergeValue));
1415
+ var newData = (0, _extends4.default)({}, (0, _fastMerge.default)(modifiedData, mergeValue));
1296
1416
 
1297
1417
  // We will also delete any object keys that are undefined or null.
1298
1418
  // Deleting keys is not supported by AsyncStorage so we do it this way.
@@ -1470,7 +1590,7 @@ function mergeCollection(collectionKey, collection) {
1470
1590
  */
1471
1591
  function update(data) {
1472
1592
  // First, validate the Onyx object is in the format we expect
1473
- _underscore.default.each(data, function (_ref5) {var onyxMethod = _ref5.onyxMethod,key = _ref5.key;
1593
+ _underscore.default.each(data, function (_ref9) {var onyxMethod = _ref9.onyxMethod,key = _ref9.key;
1474
1594
  if (!_underscore.default.contains(['clear', 'set', 'merge', 'mergecollection'], onyxMethod)) {
1475
1595
  throw new Error("Invalid onyxMethod " + onyxMethod + " in Onyx update.");
1476
1596
  }
@@ -1479,7 +1599,7 @@ function update(data) {
1479
1599
  }
1480
1600
  });
1481
1601
 
1482
- _underscore.default.each(data, function (_ref6) {var onyxMethod = _ref6.onyxMethod,key = _ref6.key,value = _ref6.value;
1602
+ _underscore.default.each(data, function (_ref10) {var onyxMethod = _ref10.onyxMethod,key = _ref10.key,value = _ref10.value;
1483
1603
  switch (onyxMethod) {
1484
1604
  case 'set':
1485
1605
  set(key, value);
@@ -1532,7 +1652,7 @@ function init()
1532
1652
 
1533
1653
 
1534
1654
 
1535
- {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$debugSetState = _ref7.debugSetState,debugSetState = _ref7$debugSetState === void 0 ? false : _ref7$debugSetState;
1655
+ {var _ref11 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},_ref11$keys = _ref11.keys,keys = _ref11$keys === void 0 ? {} : _ref11$keys,_ref11$initialKeyStat = _ref11.initialKeyStates,initialKeyStates = _ref11$initialKeyStat === void 0 ? {} : _ref11$initialKeyStat,_ref11$safeEvictionKe = _ref11.safeEvictionKeys,safeEvictionKeys = _ref11$safeEvictionKe === void 0 ? [] : _ref11$safeEvictionKe,_ref11$maxCachedKeysC = _ref11.maxCachedKeysCount,maxCachedKeysCount = _ref11$maxCachedKeysC === void 0 ? 1000 : _ref11$maxCachedKeysC,_ref11$captureMetrics = _ref11.captureMetrics,captureMetrics = _ref11$captureMetrics === void 0 ? false : _ref11$captureMetrics,_ref11$shouldSyncMult = _ref11.shouldSyncMultipleInstances,shouldSyncMultipleInstances = _ref11$shouldSyncMult === void 0 ? Boolean(__webpack_require__.g.localStorage) : _ref11$shouldSyncMult,_ref11$debugSetState = _ref11.debugSetState,debugSetState = _ref11$debugSetState === void 0 ? false : _ref11$debugSetState;
1536
1656
  if (captureMetrics) {
1537
1657
  // The code here is only bundled and applied when the captureMetrics is set
1538
1658
  // eslint-disable-next-line no-use-before-define