diff --git a/API.md b/API.md index 233c2ebc0..82b7777f4 100644 --- a/API.md +++ b/API.md @@ -5,10 +5,10 @@ ## Functions
-
getSubsetOfData(sourceData, selector)Mixed
+
getSubsetOfData(sourceData, selector, [withOnyxInstanceState])Mixed

Uses a selector string or function to return a simplified version of sourceData

-
reduceCollectionWithSelector(collection, selector)Object
+
reduceCollectionWithSelector(collection, selector, [withOnyxInstanceState])Object

Takes a collection of items (eg. {testKey_1:{a:'a'}, testKey_2:{b:'b'}}) and runs it through a reducer function to return a subset of the data according to a selector. The resulting collection will only contain items that are returned by the selector.

@@ -77,7 +77,7 @@ value will be saved to storage after the default value.

-## getSubsetOfData(sourceData, selector) ⇒ Mixed +## getSubsetOfData(sourceData, selector, [withOnyxInstanceState]) ⇒ Mixed Uses a selector string or function to return a simplified version of sourceData **Kind**: global function @@ -85,11 +85,12 @@ Uses a selector string or function to return a simplified version of sourceData | Param | Type | Description | | --- | --- | --- | | sourceData | Mixed | | -| selector | String \| function | 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 | +| selector | String \| function | | +| [withOnyxInstanceState] | Object | 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 | -## reduceCollectionWithSelector(collection, selector) ⇒ Object +## reduceCollectionWithSelector(collection, selector, [withOnyxInstanceState]) ⇒ Object Takes a collection of items (eg. {testKey_1:{a:'a'}, testKey_2:{b:'b'}}) and runs it through a reducer function to return a subset of the data according to a selector. The resulting collection will only contain items that are returned by the selector. @@ -100,6 +101,7 @@ The resulting collection will only contain items that are returned by the select | --- | --- | --- | | collection | Object | | | selector | String \| function | (see method docs for getSubsetOfData() for full details) | +| [withOnyxInstanceState] | Object | | @@ -128,7 +130,7 @@ Subscribes a react component's state directly to a store key | [mapping.callback] | function | a method that will be called with changed data This is used by any non-React code to connect to Onyx | | [mapping.initWithStoredValues] | Boolean | If set to false, then no data will be prefilled into the component | | [mapping.waitForCollectionCallback] | Boolean | If set to true, it will return the entire collection to the callback as a single object | -| [mapping.selector] | String \| function | 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). | +| [mapping.selector] | String \| function | 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 and withOnyx state are 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). | **Example** ```js diff --git a/lib/Onyx.js b/lib/Onyx.js index 43427aefa..8244dacbf 100644 --- a/lib/Onyx.js +++ b/lib/Onyx.js @@ -48,12 +48,13 @@ const deferredInitTask = createDeferredTask(); * Uses a selector string or function to return a simplified version of sourceData * @param {Mixed} sourceData * @param {String|Function} selector + * @param {Object} [withOnyxInstanceState] * 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 * @returns {Mixed} */ -const getSubsetOfData = (sourceData, selector) => (_.isFunction(selector) - ? selector(sourceData) +const getSubsetOfData = (sourceData, selector, withOnyxInstanceState) => (_.isFunction(selector) + ? selector(sourceData, withOnyxInstanceState) : lodashGet(sourceData, selector)); /** @@ -62,11 +63,12 @@ const getSubsetOfData = (sourceData, selector) => (_.isFunction(selector) * The resulting collection will only contain items that are returned by the selector. * @param {Object} collection * @param {String|Function} selector (see method docs for getSubsetOfData() for full details) + * @param {Object} [withOnyxInstanceState] * @returns {Object} */ -const reduceCollectionWithSelector = (collection, selector) => _.reduce(collection, (finalCollection, item, key) => { +const reduceCollectionWithSelector = (collection, selector, withOnyxInstanceState) => _.reduce(collection, (finalCollection, item, key) => { // eslint-disable-next-line no-param-reassign - finalCollection[key] = getSubsetOfData(item, selector); + finalCollection[key] = getSubsetOfData(item, selector, withOnyxInstanceState); return finalCollection; }, {}); @@ -362,8 +364,8 @@ function keysChanged(collectionKey, partialCollection) { // returned by the selector. if (subscriber.selector) { subscriber.withOnyxInstance.setState((prevState) => { - const previousData = reduceCollectionWithSelector(prevState[subscriber.statePropertyName], subscriber.selector); - const newData = reduceCollectionWithSelector(cachedCollection, subscriber.selector); + const previousData = reduceCollectionWithSelector(prevState[subscriber.statePropertyName], subscriber.selector, subscriber.withOnyxInstance.state); + const newData = reduceCollectionWithSelector(cachedCollection, subscriber.selector, subscriber.withOnyxInstance.state); if (!deepEqual(previousData, newData)) { return { @@ -406,7 +408,7 @@ function keysChanged(collectionKey, partialCollection) { if (subscriber.selector) { subscriber.withOnyxInstance.setState((prevState) => { const prevData = prevState[subscriber.statePropertyName]; - const newData = getSubsetOfData(cachedCollection[subscriber.key], subscriber.selector); + const newData = getSubsetOfData(cachedCollection[subscriber.key], subscriber.selector, subscriber.withOnyxInstance.state); if (!deepEqual(prevData, newData)) { PerformanceUtils.logSetStateCall(subscriber, prevData, newData, 'keysChanged', collectionKey); return { @@ -488,11 +490,11 @@ function keyChanged(key, data, canUpdateSubscriber) { subscriber.withOnyxInstance.setState((prevState) => { const prevData = prevState[subscriber.statePropertyName]; const newData = { - [key]: getSubsetOfData(data, subscriber.selector), + [key]: getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state), }; const prevDataWithNewData = { ...prevData, - [key]: getSubsetOfData(data, subscriber.selector), + [key]: getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state), }; if (!deepEqual(prevData, prevDataWithNewData)) { PerformanceUtils.logSetStateCall(subscriber, prevData, newData, 'keyChanged', key); @@ -523,8 +525,8 @@ function keyChanged(key, data, canUpdateSubscriber) { // returned by the selector and only if the selected data has changed. if (subscriber.selector) { subscriber.withOnyxInstance.setState((prevState) => { - const previousValue = getSubsetOfData(prevState[subscriber.statePropertyName], subscriber.selector); - const newValue = getSubsetOfData(data, subscriber.selector); + const previousValue = getSubsetOfData(prevState[subscriber.statePropertyName], subscriber.selector, subscriber.withOnyxInstance.state); + const newValue = getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state); if (!deepEqual(previousValue, newValue)) { return { [subscriber.statePropertyName]: newValue, @@ -582,9 +584,9 @@ function sendDataToConnection(mapping, val, matchedKey) { // returned by the selector. if (mapping.selector) { if (isCollectionKey(mapping.key)) { - newData = reduceCollectionWithSelector(val, mapping.selector); + newData = reduceCollectionWithSelector(val, mapping.selector, mapping.withOnyxInstance.state); } else { - newData = getSubsetOfData(val, mapping.selector); + newData = getSubsetOfData(val, mapping.selector, mapping.withOnyxInstance.state); } } @@ -662,9 +664,10 @@ function getCollectionDataAndSendAsObject(matchingKeys, mapping) { * component * @param {Boolean} [mapping.waitForCollectionCallback] If set to true, it will return the entire collection to the callback as a single object * @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). + * If the selector is a string, the selector is passed to lodashGet on the sourceData. If the selector is a function, 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 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). * @returns {Number} an ID to use when calling disconnect */ function connect(mapping) { diff --git a/tests/unit/subscribeToPropertiesTest.js b/tests/unit/subscribeToPropertiesTest.js index 3dc4c3a51..3af41a544 100644 --- a/tests/unit/subscribeToPropertiesTest.js +++ b/tests/unit/subscribeToPropertiesTest.js @@ -181,13 +181,18 @@ describe('Only the specific property changes when using withOnyx() and ', () => }); it('connecting to a collection with a selector function', () => { + const mockedSelector = jest.fn(obj => obj && obj.a); const TestComponentWithOnyx = withOnyx({ collectionWithPropertyA: { key: ONYX_KEYS.COLLECTION.TEST_KEY, - selector: obj => obj && obj.a, + selector: mockedSelector, }, })(ViewWithObject); - return runAllAssertionsForCollection(TestComponentWithOnyx); + return runAllAssertionsForCollection(TestComponentWithOnyx) + .then(() => { + // Check to make sure that the selector was called with the props that are passed to the rendered component + expect(mockedSelector).toHaveBeenNthCalledWith(5, {a: 'two', b: 'two'}, {loading: false, collectionWithPropertyA: {test_1: 'one', test_2: undefined}}); + }); }); /**