react-native-onyx 1.0.55 → 1.0.57
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/dist/web.development.js +218 -168
- package/dist/web.development.js.map +1 -1
- package/dist/web.min.js +1 -1
- package/dist/web.min.js.map +1 -1
- package/lib/Onyx.js +48 -13
- package/lib/withOnyx.js +35 -7
- package/package.json +1 -2
package/lib/Onyx.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/* eslint-disable no-continue */
|
|
2
2
|
import {deepEqual} from 'fast-equals';
|
|
3
|
-
import lodashGet from 'lodash/get';
|
|
4
3
|
import _ from 'underscore';
|
|
5
4
|
import * as Logger from './Logger';
|
|
6
5
|
import cache from './OnyxCache';
|
|
@@ -48,17 +47,13 @@ let defaultKeyStates = {};
|
|
|
48
47
|
const deferredInitTask = createDeferredTask();
|
|
49
48
|
|
|
50
49
|
/**
|
|
51
|
-
* Uses a selector
|
|
50
|
+
* Uses a selector function to return a simplified version of sourceData
|
|
52
51
|
* @param {Mixed} sourceData
|
|
53
|
-
* @param {
|
|
52
|
+
* @param {Function} selector Function that takes sourceData and returns a simplified version of it
|
|
54
53
|
* @param {Object} [withOnyxInstanceState]
|
|
55
|
-
* If it's a string, the selector is passed to lodashGet on the sourceData
|
|
56
|
-
* If it's a function, it is passed the sourceData and it should return the simplified data
|
|
57
54
|
* @returns {Mixed}
|
|
58
55
|
*/
|
|
59
|
-
const getSubsetOfData = (sourceData, selector, withOnyxInstanceState) => (
|
|
60
|
-
? selector(sourceData, withOnyxInstanceState)
|
|
61
|
-
: lodashGet(sourceData, selector));
|
|
56
|
+
const getSubsetOfData = (sourceData, selector, withOnyxInstanceState) => selector(sourceData, withOnyxInstanceState);
|
|
62
57
|
|
|
63
58
|
/**
|
|
64
59
|
* Takes a collection of items (eg. {testKey_1:{a:'a'}, testKey_2:{b:'b'}})
|
|
@@ -184,6 +179,46 @@ function isSafeEvictionKey(testKey) {
|
|
|
184
179
|
return _.some(evictionAllowList, key => isKeyMatch(key, testKey));
|
|
185
180
|
}
|
|
186
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
|
|
184
|
+
* If the requested key is a collection, it will return an object with all the collection members.
|
|
185
|
+
*
|
|
186
|
+
* @param {String} key
|
|
187
|
+
* @param {Object} mapping
|
|
188
|
+
* @returns {Mixed}
|
|
189
|
+
*/
|
|
190
|
+
function tryGetCachedValue(key, mapping = {}) {
|
|
191
|
+
let val = cache.getValue(key);
|
|
192
|
+
|
|
193
|
+
if (isCollectionKey(key)) {
|
|
194
|
+
const allKeys = cache.getAllKeys();
|
|
195
|
+
const matchingKeys = _.filter(allKeys, k => k.startsWith(key));
|
|
196
|
+
const values = _.reduce(matchingKeys, (finalObject, matchedKey) => {
|
|
197
|
+
const cachedValue = cache.getValue(matchedKey);
|
|
198
|
+
if (cachedValue) {
|
|
199
|
+
// This is permissible because we're in the process of constructing the final object in a reduce function.
|
|
200
|
+
// eslint-disable-next-line no-param-reassign
|
|
201
|
+
finalObject[matchedKey] = cachedValue;
|
|
202
|
+
}
|
|
203
|
+
return finalObject;
|
|
204
|
+
}, {});
|
|
205
|
+
if (_.isEmpty(values)) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
val = values;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (mapping.selector) {
|
|
212
|
+
const state = mapping.withOnyxInstance ? mapping.withOnyxInstance.state : undefined;
|
|
213
|
+
if (isCollectionKey(key)) {
|
|
214
|
+
return reduceCollectionWithSelector(val, mapping.selector, state);
|
|
215
|
+
}
|
|
216
|
+
return getSubsetOfData(val, mapping.selector, state);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return val;
|
|
220
|
+
}
|
|
221
|
+
|
|
187
222
|
/**
|
|
188
223
|
* Remove a key from the recently accessed key list.
|
|
189
224
|
*
|
|
@@ -666,11 +701,10 @@ function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
|
|
|
666
701
|
* @param {Boolean} [mapping.initWithStoredValues] If set to false, then no data will be prefilled into the
|
|
667
702
|
* component
|
|
668
703
|
* @param {Boolean} [mapping.waitForCollectionCallback] If set to true, it will return the entire collection to the callback as a single object
|
|
669
|
-
* @param {
|
|
670
|
-
*
|
|
671
|
-
*
|
|
672
|
-
*
|
|
673
|
-
* be expensive from a performance standpoint).
|
|
704
|
+
* @param {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.
|
|
705
|
+
* 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
|
|
706
|
+
* 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
|
|
707
|
+
* cause the component to re-render (and that can be expensive from a performance standpoint).
|
|
674
708
|
* @returns {Number} an ID to use when calling disconnect
|
|
675
709
|
*/
|
|
676
710
|
function connect(mapping) {
|
|
@@ -1353,6 +1387,7 @@ const Onyx = {
|
|
|
1353
1387
|
isSafeEvictionKey,
|
|
1354
1388
|
METHOD,
|
|
1355
1389
|
setMemoryOnlyKeys,
|
|
1390
|
+
tryGetCachedValue,
|
|
1356
1391
|
};
|
|
1357
1392
|
|
|
1358
1393
|
/**
|
package/lib/withOnyx.js
CHANGED
|
@@ -37,13 +37,25 @@ export default function (mapOnyxToState) {
|
|
|
37
37
|
// disconnected. It is a key value store with the format {[mapping.key]: connectionID}.
|
|
38
38
|
this.activeConnectionIDs = {};
|
|
39
39
|
|
|
40
|
+
const cachedState = _.reduce(mapOnyxToState, (resultObj, mapping, propertyName) => {
|
|
41
|
+
const key = Str.result(mapping.key, props);
|
|
42
|
+
const value = Onyx.tryGetCachedValue(key, mapping);
|
|
43
|
+
|
|
44
|
+
if (value !== undefined) {
|
|
45
|
+
// eslint-disable-next-line no-param-reassign
|
|
46
|
+
resultObj[propertyName] = value;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return resultObj;
|
|
50
|
+
}, {});
|
|
51
|
+
|
|
52
|
+
// If we have all the data we need, then we can render the component immediately
|
|
53
|
+
cachedState.loading = _.size(cachedState) < requiredKeysForInit.length;
|
|
54
|
+
|
|
40
55
|
// Object holding the temporary initial state for the component while we load the various Onyx keys
|
|
41
|
-
this.tempState =
|
|
56
|
+
this.tempState = cachedState;
|
|
42
57
|
|
|
43
|
-
this.state =
|
|
44
|
-
// If there are no required keys for init then we can render the wrapped component immediately
|
|
45
|
-
loading: requiredKeysForInit.length > 0,
|
|
46
|
-
};
|
|
58
|
+
this.state = cachedState;
|
|
47
59
|
}
|
|
48
60
|
|
|
49
61
|
componentDidMount() {
|
|
@@ -60,7 +72,6 @@ export default function (mapOnyxToState) {
|
|
|
60
72
|
_.each(mapOnyxToState, (mapping, propertyName) => {
|
|
61
73
|
const previousKey = Str.result(mapping.key, prevProps);
|
|
62
74
|
const newKey = Str.result(mapping.key, this.props);
|
|
63
|
-
|
|
64
75
|
if (previousKey !== newKey) {
|
|
65
76
|
Onyx.disconnect(this.activeConnectionIDs[previousKey], previousKey);
|
|
66
77
|
delete this.activeConnectionIDs[previousKey];
|
|
@@ -88,6 +99,16 @@ export default function (mapOnyxToState) {
|
|
|
88
99
|
* @param {*} val
|
|
89
100
|
*/
|
|
90
101
|
setWithOnyxState(statePropertyName, val) {
|
|
102
|
+
// We might have loaded the values for the onyx keys/mappings already from the cache.
|
|
103
|
+
// In case we were able to load all the values upfront, the loading state will be false.
|
|
104
|
+
// However, Onyx.js will always call setWithOnyxState, as it doesn't know that this implementation
|
|
105
|
+
// already loaded the values from cache. Thus we have to check whether the value has changed
|
|
106
|
+
// before we set the state to prevent unnecessary renders.
|
|
107
|
+
const prevValue = this.state[statePropertyName];
|
|
108
|
+
if (!this.state.loading && prevValue === val) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
91
112
|
if (!this.state.loading) {
|
|
92
113
|
this.setState({[statePropertyName]: val});
|
|
93
114
|
return;
|
|
@@ -100,7 +121,14 @@ export default function (mapOnyxToState) {
|
|
|
100
121
|
return;
|
|
101
122
|
}
|
|
102
123
|
|
|
103
|
-
|
|
124
|
+
const stateUpdate = {...this.tempState, loading: false};
|
|
125
|
+
|
|
126
|
+
// The state is set here manually, instead of using setState because setState is an async operation, meaning it might execute on the next tick,
|
|
127
|
+
// or at a later point in the microtask queue. That can lead to a race condition where
|
|
128
|
+
// setWithOnyxState is called before the state is actually set. This results in unreliable behavior when checking the loading state and has been mainly observed on fabric.
|
|
129
|
+
this.state = stateUpdate;
|
|
130
|
+
|
|
131
|
+
this.setState(stateUpdate); // Trigger a render
|
|
104
132
|
delete this.tempState;
|
|
105
133
|
}
|
|
106
134
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-onyx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.57",
|
|
4
4
|
"author": "Expensify, Inc.",
|
|
5
5
|
"homepage": "https://expensify.com",
|
|
6
6
|
"description": "State management for React Native",
|
|
@@ -41,7 +41,6 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"ascii-table": "0.0.9",
|
|
43
43
|
"fast-equals": "^4.0.3",
|
|
44
|
-
"lodash": "^4.17.21",
|
|
45
44
|
"underscore": "^1.13.1"
|
|
46
45
|
},
|
|
47
46
|
"devDependencies": {
|