react-native-onyx 1.0.120 → 1.0.122
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 +1 -0
- package/dist/web.development.js +118 -110
- package/dist/web.development.js.map +1 -1
- package/dist/web.min.js.map +1 -1
- package/lib/Logger.js +1 -5
- package/lib/MDTable.js +11 -14
- package/lib/Onyx.d.ts +1 -4
- package/lib/Onyx.js +237 -232
- package/lib/OnyxCache.js +12 -3
- package/lib/Str.js +1 -3
- package/lib/compose.js +6 -2
- package/lib/metrics/PerformanceUtils.js +2 -7
- package/lib/metrics/index.native.js +28 -41
- package/lib/metrics/index.web.js +4 -7
- package/lib/storage/WebStorage.js +5 -10
- package/lib/storage/__mocks__/index.js +2 -2
- package/lib/storage/providers/IDBKeyVal.js +27 -37
- package/lib/storage/providers/SQLiteStorage.js +58 -62
- package/lib/types.d.ts +1 -13
- package/lib/utils.d.ts +2 -6
- package/lib/utils.js +19 -22
- package/lib/withOnyx.d.ts +8 -32
- package/lib/withOnyx.js +37 -34
- package/package.json +5 -3
package/lib/utils.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import _ from 'underscore';
|
|
2
2
|
|
|
3
3
|
function areObjectsEmpty(a, b) {
|
|
4
|
-
return (
|
|
5
|
-
typeof a === 'object'
|
|
6
|
-
&& typeof b === 'object'
|
|
7
|
-
&& _.isEmpty(a)
|
|
8
|
-
&& _.isEmpty(b)
|
|
9
|
-
);
|
|
4
|
+
return typeof a === 'object' && typeof b === 'object' && _.isEmpty(a) && _.isEmpty(b);
|
|
10
5
|
}
|
|
11
6
|
|
|
12
7
|
// Mostly copied from https://medium.com/@lubaka.a/how-to-remove-lodash-performance-improvement-b306669ad0e1
|
|
@@ -14,22 +9,24 @@ function areObjectsEmpty(a, b) {
|
|
|
14
9
|
/**
|
|
15
10
|
* @param {mixed} val
|
|
16
11
|
* @returns {boolean}
|
|
17
|
-
*/
|
|
12
|
+
*/
|
|
18
13
|
function isMergeableObject(val) {
|
|
19
14
|
const nonNullObject = val != null ? typeof val === 'object' : false;
|
|
20
|
-
return (
|
|
21
|
-
&&
|
|
22
|
-
|
|
15
|
+
return (
|
|
16
|
+
nonNullObject &&
|
|
17
|
+
Object.prototype.toString.call(val) !== '[object RegExp]' &&
|
|
18
|
+
Object.prototype.toString.call(val) !== '[object Date]' &&
|
|
23
19
|
// eslint-disable-next-line rulesdir/prefer-underscore-method
|
|
24
|
-
|
|
20
|
+
!Array.isArray(val)
|
|
21
|
+
);
|
|
25
22
|
}
|
|
26
23
|
|
|
27
24
|
/**
|
|
28
|
-
* @param {Object} target
|
|
29
|
-
* @param {Object} source
|
|
30
|
-
* @param {Boolean} shouldRemoveNullObjectValues
|
|
31
|
-
* @returns {Object}
|
|
32
|
-
*/
|
|
25
|
+
* @param {Object} target
|
|
26
|
+
* @param {Object} source
|
|
27
|
+
* @param {Boolean} shouldRemoveNullObjectValues
|
|
28
|
+
* @returns {Object}
|
|
29
|
+
*/
|
|
33
30
|
function mergeObject(target, source, shouldRemoveNullObjectValues = true) {
|
|
34
31
|
const destination = {};
|
|
35
32
|
if (isMergeableObject(target)) {
|
|
@@ -65,7 +62,7 @@ function mergeObject(target, source, shouldRemoveNullObjectValues = true) {
|
|
|
65
62
|
const isSourceKeyMergable = isMergeableObject(source[key]);
|
|
66
63
|
|
|
67
64
|
if (isSourceKeyMergable && target[key]) {
|
|
68
|
-
if (
|
|
65
|
+
if (!shouldRemoveNullObjectValues || isSourceKeyMergable) {
|
|
69
66
|
// eslint-disable-next-line no-use-before-define
|
|
70
67
|
destination[key] = fastMerge(target[key], source[key], shouldRemoveNullObjectValues);
|
|
71
68
|
}
|
|
@@ -85,11 +82,11 @@ function mergeObject(target, source, shouldRemoveNullObjectValues = true) {
|
|
|
85
82
|
* On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values.
|
|
86
83
|
* To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations.
|
|
87
84
|
*
|
|
88
|
-
* @param {Object|Array} target
|
|
89
|
-
* @param {Object|Array} source
|
|
90
|
-
* @param {Boolean} shouldRemoveNullObjectValues
|
|
91
|
-
* @returns {Object|Array}
|
|
92
|
-
*/
|
|
85
|
+
* @param {Object|Array} target
|
|
86
|
+
* @param {Object|Array} source
|
|
87
|
+
* @param {Boolean} shouldRemoveNullObjectValues
|
|
88
|
+
* @returns {Object|Array}
|
|
89
|
+
*/
|
|
93
90
|
function fastMerge(target, source, shouldRemoveNullObjectValues = true) {
|
|
94
91
|
// We have to ignore arrays and nullish values here,
|
|
95
92
|
// otherwise "mergeObject" will throw an error,
|
package/lib/withOnyx.d.ts
CHANGED
|
@@ -40,13 +40,7 @@ type EntryBaseMapping<TOnyxKey extends OnyxKey> = {
|
|
|
40
40
|
* },
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
|
-
type BaseMappingKey<
|
|
44
|
-
TComponentProps,
|
|
45
|
-
TOnyxProps,
|
|
46
|
-
TOnyxProp extends keyof TOnyxProps,
|
|
47
|
-
TOnyxKey extends OnyxKey,
|
|
48
|
-
TOnyxValue
|
|
49
|
-
> = IsEqual<TOnyxValue, TOnyxProps[TOnyxProp]> extends true
|
|
43
|
+
type BaseMappingKey<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TOnyxKey extends OnyxKey, TOnyxValue> = IsEqual<TOnyxValue, TOnyxProps[TOnyxProp]> extends true
|
|
50
44
|
? {
|
|
51
45
|
key: TOnyxKey | ((props: Omit<TComponentProps, keyof TOnyxProps>) => TOnyxKey);
|
|
52
46
|
}
|
|
@@ -67,12 +61,7 @@ type BaseMappingKey<
|
|
|
67
61
|
* },
|
|
68
62
|
* ```
|
|
69
63
|
*/
|
|
70
|
-
type BaseMappingStringKeyAndSelector<
|
|
71
|
-
TComponentProps,
|
|
72
|
-
TOnyxProps,
|
|
73
|
-
TOnyxProp extends keyof TOnyxProps,
|
|
74
|
-
TOnyxKey extends OnyxKey
|
|
75
|
-
> = {
|
|
64
|
+
type BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TOnyxKey extends OnyxKey> = {
|
|
76
65
|
key: TOnyxKey;
|
|
77
66
|
selector: Selector<TOnyxKey, TOnyxProps[TOnyxProp]>;
|
|
78
67
|
};
|
|
@@ -92,12 +81,7 @@ type BaseMappingStringKeyAndSelector<
|
|
|
92
81
|
* },
|
|
93
82
|
* ```
|
|
94
83
|
*/
|
|
95
|
-
type BaseMappingFunctionKeyAndSelector<
|
|
96
|
-
TComponentProps,
|
|
97
|
-
TOnyxProps,
|
|
98
|
-
TOnyxProp extends keyof TOnyxProps,
|
|
99
|
-
TOnyxKey extends OnyxKey
|
|
100
|
-
> = {
|
|
84
|
+
type BaseMappingFunctionKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TOnyxKey extends OnyxKey> = {
|
|
101
85
|
key: (props: Omit<TComponentProps, keyof TOnyxProps>) => TOnyxKey;
|
|
102
86
|
selector: Selector<TOnyxKey, TOnyxProps[TOnyxProp]>;
|
|
103
87
|
};
|
|
@@ -105,10 +89,8 @@ type BaseMappingFunctionKeyAndSelector<
|
|
|
105
89
|
/**
|
|
106
90
|
* Represents the mapping options between an Onyx key and the component's prop with all its possibilities.
|
|
107
91
|
*/
|
|
108
|
-
type Mapping<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TOnyxKey extends OnyxKey> = BaseMapping<
|
|
109
|
-
|
|
110
|
-
TOnyxProps
|
|
111
|
-
> & EntryBaseMapping<TOnyxKey> &
|
|
92
|
+
type Mapping<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TOnyxKey extends OnyxKey> = BaseMapping<TComponentProps, TOnyxProps> &
|
|
93
|
+
EntryBaseMapping<TOnyxKey> &
|
|
112
94
|
(
|
|
113
95
|
| BaseMappingKey<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey, OnyxEntry<KeyValueMapping[TOnyxKey]>>
|
|
114
96
|
| BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey>
|
|
@@ -118,12 +100,8 @@ type Mapping<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TO
|
|
|
118
100
|
/**
|
|
119
101
|
* Represents the mapping options between an Onyx collection key without suffix and the component's prop with all its possibilities.
|
|
120
102
|
*/
|
|
121
|
-
type CollectionMapping<
|
|
122
|
-
|
|
123
|
-
TOnyxProps,
|
|
124
|
-
TOnyxProp extends keyof TOnyxProps,
|
|
125
|
-
TOnyxKey extends CollectionKeyBase
|
|
126
|
-
> = BaseMapping<TComponentProps, TOnyxProps> & CollectionBaseMapping<TOnyxKey> &
|
|
103
|
+
type CollectionMapping<TComponentProps, TOnyxProps, TOnyxProp extends keyof TOnyxProps, TOnyxKey extends CollectionKeyBase> = BaseMapping<TComponentProps, TOnyxProps> &
|
|
104
|
+
CollectionBaseMapping<TOnyxKey> &
|
|
127
105
|
(
|
|
128
106
|
| BaseMappingKey<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey, OnyxCollection<KeyValueMapping[TOnyxKey]>>
|
|
129
107
|
| BaseMappingStringKeyAndSelector<TComponentProps, TOnyxProps, TOnyxProp, TOnyxKey>
|
|
@@ -153,9 +131,7 @@ type OnyxPropCollectionMapping<TComponentProps, TOnyxProps, TOnyxProp extends ke
|
|
|
153
131
|
*/
|
|
154
132
|
declare function withOnyx<TComponentProps, TOnyxProps>(
|
|
155
133
|
mapping: {
|
|
156
|
-
[TOnyxProp in keyof TOnyxProps]:
|
|
157
|
-
| OnyxPropMapping<TComponentProps, TOnyxProps, TOnyxProp>
|
|
158
|
-
| OnyxPropCollectionMapping<TComponentProps, TOnyxProps, TOnyxProp>;
|
|
134
|
+
[TOnyxProp in keyof TOnyxProps]: OnyxPropMapping<TComponentProps, TOnyxProps, TOnyxProp> | OnyxPropCollectionMapping<TComponentProps, TOnyxProps, TOnyxProp>;
|
|
159
135
|
},
|
|
160
136
|
shouldDelayUpdates?: boolean,
|
|
161
137
|
): (component: React.ComponentType<TComponentProps>) => React.ComponentType<Omit<TComponentProps, keyof TOnyxProps>>;
|
package/lib/withOnyx.js
CHANGED
|
@@ -36,7 +36,7 @@ const getOnyxDataFromState = (state, onyxToStateMapping) => _.pick(state, _.keys
|
|
|
36
36
|
export default function (mapOnyxToState, shouldDelayUpdates = false) {
|
|
37
37
|
// A list of keys that must be present in tempState before we can render the WrappedComponent
|
|
38
38
|
const requiredKeysForInit = _.chain(mapOnyxToState)
|
|
39
|
-
.omit(config => config.initWithStoredValues === false)
|
|
39
|
+
.omit((config) => config.initWithStoredValues === false)
|
|
40
40
|
.keys()
|
|
41
41
|
.value();
|
|
42
42
|
return (WrappedComponent) => {
|
|
@@ -74,11 +74,7 @@ export default function (mapOnyxToState, shouldDelayUpdates = false) {
|
|
|
74
74
|
* In reality, Onyx.merge() will only update the subscriber after all merges have been batched and the previous value is retrieved via a get() (returns a promise).
|
|
75
75
|
* So, we won't use the cache optimization here as it will lead us to arbitrarily defer various actions in the application code.
|
|
76
76
|
*/
|
|
77
|
-
if (
|
|
78
|
-
(value !== undefined
|
|
79
|
-
&& !Onyx.hasPendingMergeForKey(key))
|
|
80
|
-
|| mapping.allowStaleData
|
|
81
|
-
) {
|
|
77
|
+
if ((value !== undefined && !Onyx.hasPendingMergeForKey(key)) || mapping.allowStaleData) {
|
|
82
78
|
// eslint-disable-next-line no-param-reassign
|
|
83
79
|
resultObj[propertyName] = value;
|
|
84
80
|
}
|
|
@@ -132,9 +128,7 @@ export default function (mapOnyxToState, shouldDelayUpdates = false) {
|
|
|
132
128
|
// (eg. if a user switches chats really quickly). In this case, it's much more stable to always look at the changes to prevProp and prevState to derive the key.
|
|
133
129
|
// The second case cannot be used all the time because the onyx data doesn't change the first time that `componentDidUpdate()` runs after loading. In this case,
|
|
134
130
|
// the `mapping.previousKey` must be used for the comparison or else this logic never detects that onyx data could have changed during the loading process.
|
|
135
|
-
const previousKey = isFirstTimeUpdatingAfterLoading
|
|
136
|
-
? mapping.previousKey
|
|
137
|
-
: Str.result(mapping.key, {...prevProps, ...prevOnyxDataFromState});
|
|
131
|
+
const previousKey = isFirstTimeUpdatingAfterLoading ? mapping.previousKey : Str.result(mapping.key, {...prevProps, ...prevOnyxDataFromState});
|
|
138
132
|
const newKey = Str.result(mapping.key, {...this.props, ...onyxDataFromState});
|
|
139
133
|
if (previousKey !== newKey) {
|
|
140
134
|
Onyx.disconnect(this.activeConnectionIDs[previousKey], previousKey);
|
|
@@ -202,7 +196,7 @@ export default function (mapOnyxToState, shouldDelayUpdates = false) {
|
|
|
202
196
|
this.tempState[statePropertyName] = val;
|
|
203
197
|
|
|
204
198
|
// If some key does not have a value yet, do not update the state yet
|
|
205
|
-
const tempStateIsMissingKey = _.some(requiredKeysForInit, key => _.isUndefined(this.tempState[key]));
|
|
199
|
+
const tempStateIsMissingKey = _.some(requiredKeysForInit, (key) => _.isUndefined(this.tempState[key]));
|
|
206
200
|
if (tempStateIsMissingKey) {
|
|
207
201
|
return;
|
|
208
202
|
}
|
|
@@ -212,29 +206,33 @@ export default function (mapOnyxToState, shouldDelayUpdates = false) {
|
|
|
212
206
|
|
|
213
207
|
// Full of hacky workarounds to prevent the race condition described above.
|
|
214
208
|
this.setState((prevState) => {
|
|
215
|
-
const finalState = _.reduce(
|
|
216
|
-
|
|
209
|
+
const finalState = _.reduce(
|
|
210
|
+
stateUpdate,
|
|
211
|
+
(result, value, key) => {
|
|
212
|
+
if (key === 'loading') {
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const initialValue = mapOnyxToState[key].initialValue;
|
|
217
|
+
|
|
218
|
+
// If initialValue is there and the state contains something different it means
|
|
219
|
+
// an update has already been received and we can discard the value we are trying to hydrate
|
|
220
|
+
if (!_.isUndefined(initialValue) && !_.isUndefined(prevState[key]) && prevState[key] !== initialValue) {
|
|
221
|
+
// eslint-disable-next-line no-param-reassign
|
|
222
|
+
result[key] = prevState[key];
|
|
223
|
+
|
|
224
|
+
// if value is already there (without initial value) then we can discard the value we are trying to hydrate
|
|
225
|
+
} else if (!_.isUndefined(prevState[key])) {
|
|
226
|
+
// eslint-disable-next-line no-param-reassign
|
|
227
|
+
result[key] = prevState[key];
|
|
228
|
+
} else {
|
|
229
|
+
// eslint-disable-next-line no-param-reassign
|
|
230
|
+
result[key] = value;
|
|
231
|
+
}
|
|
217
232
|
return result;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// If initialValue is there and the state contains something different it means
|
|
223
|
-
// an update has already been received and we can discard the value we are trying to hydrate
|
|
224
|
-
if (!_.isUndefined(initialValue) && !_.isUndefined(prevState[key]) && prevState[key] !== initialValue) {
|
|
225
|
-
// eslint-disable-next-line no-param-reassign
|
|
226
|
-
result[key] = prevState[key];
|
|
227
|
-
|
|
228
|
-
// if value is already there (without initial value) then we can discard the value we are trying to hydrate
|
|
229
|
-
} else if (!_.isUndefined(prevState[key])) {
|
|
230
|
-
// eslint-disable-next-line no-param-reassign
|
|
231
|
-
result[key] = prevState[key];
|
|
232
|
-
} else {
|
|
233
|
-
// eslint-disable-next-line no-param-reassign
|
|
234
|
-
result[key] = value;
|
|
235
|
-
}
|
|
236
|
-
return result;
|
|
237
|
-
}, {});
|
|
233
|
+
},
|
|
234
|
+
{},
|
|
235
|
+
);
|
|
238
236
|
|
|
239
237
|
finalState.loading = false;
|
|
240
238
|
return finalState;
|
|
@@ -350,8 +348,13 @@ export default function (mapOnyxToState, shouldDelayUpdates = false) {
|
|
|
350
348
|
withOnyx.displayName = `withOnyx(${displayName})`;
|
|
351
349
|
return React.forwardRef((props, ref) => {
|
|
352
350
|
const Component = withOnyx;
|
|
353
|
-
|
|
354
|
-
|
|
351
|
+
return (
|
|
352
|
+
<Component
|
|
353
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
354
|
+
{...props}
|
|
355
|
+
forwardedRef={ref}
|
|
356
|
+
/>
|
|
357
|
+
);
|
|
355
358
|
});
|
|
356
359
|
};
|
|
357
360
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-onyx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.122",
|
|
4
4
|
"author": "Expensify, Inc.",
|
|
5
5
|
"homepage": "https://expensify.com",
|
|
6
6
|
"description": "State management for React Native",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"build:docs": "node buildDocs.js",
|
|
41
41
|
"e2e": "playwright test",
|
|
42
42
|
"e2e-ui": "playwright test --ui",
|
|
43
|
-
"
|
|
43
|
+
"prettier": "prettier --write ."
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"ascii-table": "0.0.9",
|
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
"babel-jest": "^26.2.2",
|
|
62
62
|
"babel-loader": "^8.2.5",
|
|
63
63
|
"eslint": "^7.6.0",
|
|
64
|
-
"eslint-config-expensify": "^2.0.
|
|
64
|
+
"eslint-config-expensify": "^2.0.42",
|
|
65
|
+
"eslint-config-prettier": "^8.8.0",
|
|
65
66
|
"eslint-plugin-jsx-a11y": "^6.6.1",
|
|
66
67
|
"eslint-plugin-react": "^7.31.10",
|
|
67
68
|
"idb-keyval": "^6.2.1",
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"jest-cli": "^26.5.2",
|
|
70
71
|
"jsdoc-to-markdown": "^7.1.0",
|
|
71
72
|
"metro-react-native-babel-preset": "^0.72.3",
|
|
73
|
+
"prettier": "^2.8.8",
|
|
72
74
|
"prop-types": "^15.7.2",
|
|
73
75
|
"react": "18.2.0",
|
|
74
76
|
"react-dom": "^18.2.0",
|