react-native-onyx 1.0.119 → 1.0.121
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 +7 -0
- package/dist/web.development.js +408 -113
- 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/ActiveClientManager/index.d.ts +22 -0
- package/lib/ActiveClientManager/index.native.js +23 -0
- package/lib/ActiveClientManager/index.web.js +99 -0
- package/lib/Logger.js +1 -5
- package/lib/MDTable.js +11 -14
- package/lib/Onyx.d.ts +11 -4
- package/lib/Onyx.js +344 -232
- package/lib/OnyxCache.js +12 -3
- package/lib/Str.js +17 -4
- package/lib/broadcast/index.d.ts +17 -0
- package/lib/broadcast/index.native.js +14 -0
- package/lib/broadcast/index.web.js +35 -0
- 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 +6 -11
- 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 +15 -6
|
@@ -19,10 +19,10 @@ db.execute('PRAGMA journal_mode=WAL;');
|
|
|
19
19
|
|
|
20
20
|
const provider = {
|
|
21
21
|
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
* Get the value of a given key or return `null` if it's not available in storage
|
|
23
|
+
* @param {String} key
|
|
24
|
+
* @return {Promise<*>}
|
|
25
|
+
*/
|
|
26
26
|
getItem(key) {
|
|
27
27
|
return db.executeAsync('SELECT record_key, valueJSON FROM keyvaluepairs WHERE record_key = ?;', [key]).then(({rows}) => {
|
|
28
28
|
if (rows.length === 0) {
|
|
@@ -34,41 +34,37 @@ const provider = {
|
|
|
34
34
|
},
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
* Get multiple key-value pairs for the given array of keys in a batch
|
|
38
|
+
* @param {String[]} keys
|
|
39
|
+
* @return {Promise<Array<[key, value]>>}
|
|
40
|
+
*/
|
|
41
41
|
multiGet(keys) {
|
|
42
42
|
const placeholders = _.map(keys, () => '?').join(',');
|
|
43
43
|
const command = `SELECT record_key, valueJSON FROM keyvaluepairs WHERE record_key IN (${placeholders});`;
|
|
44
|
-
return db.executeAsync(command, keys)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
44
|
+
return db.executeAsync(command, keys).then(({rows}) => {
|
|
45
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
46
|
+
const result = _.map(rows._array, (row) => [row.record_key, JSON.parse(row.valueJSON)]);
|
|
47
|
+
return result;
|
|
48
|
+
});
|
|
50
49
|
},
|
|
51
50
|
|
|
52
51
|
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
* Sets the value for a given key. The only requirement is that the value should be serializable to JSON string
|
|
53
|
+
* @param {String} key
|
|
54
|
+
* @param {*} value
|
|
55
|
+
* @return {Promise<void>}
|
|
56
|
+
*/
|
|
58
57
|
setItem(key, value) {
|
|
59
58
|
return db.executeAsync('REPLACE INTO keyvaluepairs (record_key, valueJSON) VALUES (?, ?);', [key, JSON.stringify(value)]);
|
|
60
59
|
},
|
|
61
60
|
|
|
62
61
|
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
* Stores multiple key-value pairs in a batch
|
|
63
|
+
* @param {Array<[key, value]>} pairs
|
|
64
|
+
* @return {Promise<void>}
|
|
65
|
+
*/
|
|
67
66
|
multiSet(pairs) {
|
|
68
|
-
const stringifiedPairs = _.map(pairs, pair => [
|
|
69
|
-
pair[0],
|
|
70
|
-
JSON.stringify(_.isUndefined(pair[1]) ? null : pair[1]),
|
|
71
|
-
]);
|
|
67
|
+
const stringifiedPairs = _.map(pairs, (pair) => [pair[0], JSON.stringify(_.isUndefined(pair[1]) ? null : pair[1])]);
|
|
72
68
|
if (_.isEmpty(stringifiedPairs)) {
|
|
73
69
|
return Promise.resolve();
|
|
74
70
|
}
|
|
@@ -76,10 +72,10 @@ const provider = {
|
|
|
76
72
|
},
|
|
77
73
|
|
|
78
74
|
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
* Multiple merging of existing and new values in a batch
|
|
76
|
+
* @param {Array<[key, value]>} pairs
|
|
77
|
+
* @return {Promise<void>}
|
|
78
|
+
*/
|
|
83
79
|
multiMerge(pairs) {
|
|
84
80
|
// Note: We use `ON CONFLICT DO UPDATE` here instead of `INSERT OR REPLACE INTO`
|
|
85
81
|
// so the new JSON value is merged into the old one if there's an existing value
|
|
@@ -89,7 +85,7 @@ const provider = {
|
|
|
89
85
|
SET valueJSON = JSON_PATCH(valueJSON, JSON(:value));
|
|
90
86
|
`;
|
|
91
87
|
|
|
92
|
-
const nonNullishPairs = _.filter(pairs, pair => !_.isUndefined(pair[1]));
|
|
88
|
+
const nonNullishPairs = _.filter(pairs, (pair) => !_.isUndefined(pair[1]));
|
|
93
89
|
const queryArguments = _.map(nonNullishPairs, (pair) => {
|
|
94
90
|
const value = JSON.stringify(pair[1]);
|
|
95
91
|
return [pair[0], value];
|
|
@@ -99,31 +95,32 @@ const provider = {
|
|
|
99
95
|
},
|
|
100
96
|
|
|
101
97
|
/**
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
98
|
+
* Merges an existing value with a new one by leveraging JSON_PATCH
|
|
99
|
+
* @param {String} key
|
|
100
|
+
* @param {*} changes - the delta for a specific key
|
|
101
|
+
* @return {Promise<void>}
|
|
102
|
+
*/
|
|
107
103
|
mergeItem(key, changes) {
|
|
108
104
|
return this.multiMerge([[key, changes]]);
|
|
109
105
|
},
|
|
110
106
|
|
|
111
107
|
/**
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
getAllKeys: () =>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
108
|
+
* Returns all keys available in storage
|
|
109
|
+
* @returns {Promise<String[]>}
|
|
110
|
+
*/
|
|
111
|
+
getAllKeys: () =>
|
|
112
|
+
db.executeAsync('SELECT record_key FROM keyvaluepairs;').then(({rows}) => {
|
|
113
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
114
|
+
const result = _.map(rows._array, (row) => row.record_key);
|
|
115
|
+
return result;
|
|
116
|
+
}),
|
|
120
117
|
|
|
121
118
|
/**
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
removeItem: key => db.executeAsync('DELETE FROM keyvaluepairs WHERE record_key = ?;', [key]),
|
|
119
|
+
* Removes given key and it's value from storage
|
|
120
|
+
* @param {String} key
|
|
121
|
+
* @returns {Promise<void>}
|
|
122
|
+
*/
|
|
123
|
+
removeItem: (key) => db.executeAsync('DELETE FROM keyvaluepairs WHERE record_key = ?;', [key]),
|
|
127
124
|
|
|
128
125
|
/**
|
|
129
126
|
* Removes given keys and their values from storage
|
|
@@ -138,9 +135,9 @@ const provider = {
|
|
|
138
135
|
},
|
|
139
136
|
|
|
140
137
|
/**
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
* Clears absolutely everything from storage
|
|
139
|
+
* @returns {Promise<void>}
|
|
140
|
+
*/
|
|
144
141
|
clear: () => db.executeAsync('DELETE FROM keyvaluepairs;', []),
|
|
145
142
|
|
|
146
143
|
/**
|
|
@@ -153,15 +150,14 @@ const provider = {
|
|
|
153
150
|
* @returns {Promise}
|
|
154
151
|
*/
|
|
155
152
|
getDatabaseSize() {
|
|
156
|
-
return Promise.all([db.executeAsync('PRAGMA page_size;'), db.executeAsync('PRAGMA page_count;'), getFreeDiskStorage()])
|
|
157
|
-
.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
});
|
|
153
|
+
return Promise.all([db.executeAsync('PRAGMA page_size;'), db.executeAsync('PRAGMA page_count;'), getFreeDiskStorage()]).then(([pageSizeResult, pageCountResult, bytesRemaining]) => {
|
|
154
|
+
const pageSize = pageSizeResult.rows.item(0).page_size;
|
|
155
|
+
const pageCount = pageCountResult.rows.item(0).page_count;
|
|
156
|
+
return {
|
|
157
|
+
bytesUsed: pageSize * pageCount,
|
|
158
|
+
bytesRemaining,
|
|
159
|
+
};
|
|
160
|
+
});
|
|
165
161
|
},
|
|
166
162
|
|
|
167
163
|
/**
|
package/lib/types.d.ts
CHANGED
|
@@ -221,16 +221,4 @@ type NullishObjectDeep<ObjectType extends object> = {
|
|
|
221
221
|
[KeyType in keyof ObjectType]?: NullishDeep<ObjectType[KeyType]> | null;
|
|
222
222
|
};
|
|
223
223
|
|
|
224
|
-
export {
|
|
225
|
-
CollectionKey,
|
|
226
|
-
CollectionKeyBase,
|
|
227
|
-
CustomTypeOptions,
|
|
228
|
-
DeepRecord,
|
|
229
|
-
Key,
|
|
230
|
-
KeyValueMapping,
|
|
231
|
-
OnyxCollection,
|
|
232
|
-
OnyxEntry,
|
|
233
|
-
OnyxKey,
|
|
234
|
-
Selector,
|
|
235
|
-
NullishDeep,
|
|
236
|
-
};
|
|
224
|
+
export {CollectionKey, CollectionKeyBase, CustomTypeOptions, DeepRecord, Key, KeyValueMapping, OnyxCollection, OnyxEntry, OnyxKey, Selector, NullishDeep};
|
package/lib/utils.d.ts
CHANGED
|
@@ -5,10 +5,6 @@
|
|
|
5
5
|
* On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values.
|
|
6
6
|
* To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations.
|
|
7
7
|
*/
|
|
8
|
-
declare function fastMerge<T>(
|
|
9
|
-
target: T,
|
|
10
|
-
source: T,
|
|
11
|
-
shouldRemoveNullObjectValues: boolean = true
|
|
12
|
-
): T;
|
|
8
|
+
declare function fastMerge<T>(target: T, source: T, shouldRemoveNullObjectValues: boolean = true): T;
|
|
13
9
|
|
|
14
|
-
export default {
|
|
10
|
+
export default {fastMerge};
|
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.121",
|
|
4
4
|
"author": "Expensify, Inc.",
|
|
5
5
|
"homepage": "https://expensify.com",
|
|
6
6
|
"description": "State management for React Native",
|
|
@@ -37,7 +37,11 @@
|
|
|
37
37
|
"lint-tests": "eslint tests/**",
|
|
38
38
|
"test": "jest",
|
|
39
39
|
"build": "webpack --config webpack.config.js",
|
|
40
|
-
"build:docs": "node buildDocs.js"
|
|
40
|
+
"build:docs": "node buildDocs.js",
|
|
41
|
+
"e2e": "playwright test",
|
|
42
|
+
"e2e-ui": "playwright test --ui",
|
|
43
|
+
"postinstall": "cd tests/e2e/app && npm install",
|
|
44
|
+
"prettier": "prettier --write ."
|
|
41
45
|
},
|
|
42
46
|
"dependencies": {
|
|
43
47
|
"ascii-table": "0.0.9",
|
|
@@ -48,15 +52,18 @@
|
|
|
48
52
|
"@babel/core": "7.20.12",
|
|
49
53
|
"@babel/preset-env": "^7.20.2",
|
|
50
54
|
"@babel/preset-react": "^7.18.6",
|
|
55
|
+
"@playwright/test": "^1.38.1",
|
|
51
56
|
"@react-native-community/eslint-config": "^2.0.0",
|
|
52
57
|
"@testing-library/jest-native": "^3.4.2",
|
|
53
58
|
"@testing-library/react-native": "^7.0.2",
|
|
59
|
+
"@types/node": "^20.7.1",
|
|
54
60
|
"@types/react": "^18.2.14",
|
|
55
61
|
"babel-eslint": "^10.1.0",
|
|
56
62
|
"babel-jest": "^26.2.2",
|
|
57
63
|
"babel-loader": "^8.2.5",
|
|
58
64
|
"eslint": "^7.6.0",
|
|
59
|
-
"eslint-config-expensify": "^2.0.
|
|
65
|
+
"eslint-config-expensify": "^2.0.42",
|
|
66
|
+
"eslint-config-prettier": "^8.8.0",
|
|
60
67
|
"eslint-plugin-jsx-a11y": "^6.6.1",
|
|
61
68
|
"eslint-plugin-react": "^7.31.10",
|
|
62
69
|
"idb-keyval": "^6.2.1",
|
|
@@ -64,6 +71,7 @@
|
|
|
64
71
|
"jest-cli": "^26.5.2",
|
|
65
72
|
"jsdoc-to-markdown": "^7.1.0",
|
|
66
73
|
"metro-react-native-babel-preset": "^0.72.3",
|
|
74
|
+
"prettier": "^2.8.8",
|
|
67
75
|
"prop-types": "^15.7.2",
|
|
68
76
|
"react": "18.2.0",
|
|
69
77
|
"react-dom": "^18.2.0",
|
|
@@ -81,9 +89,9 @@
|
|
|
81
89
|
"idb-keyval": "^6.2.1",
|
|
82
90
|
"react": ">=18.1.0",
|
|
83
91
|
"react-dom": ">=18.1.0",
|
|
92
|
+
"react-native-device-info": "^10.3.0",
|
|
84
93
|
"react-native-performance": "^5.1.0",
|
|
85
|
-
"react-native-quick-sqlite": "^8.0.0-beta.2"
|
|
86
|
-
"react-native-device-info": "^10.3.0"
|
|
94
|
+
"react-native-quick-sqlite": "^8.0.0-beta.2"
|
|
87
95
|
},
|
|
88
96
|
"peerDependenciesMeta": {
|
|
89
97
|
"idb-keyval": {
|
|
@@ -113,7 +121,8 @@
|
|
|
113
121
|
],
|
|
114
122
|
"testPathIgnorePatterns": [
|
|
115
123
|
"<rootDir>/node_modules/",
|
|
116
|
-
"<rootDir>/tests/unit/mocks/"
|
|
124
|
+
"<rootDir>/tests/unit/mocks/",
|
|
125
|
+
"<rootDir>/tests/e2e/"
|
|
117
126
|
],
|
|
118
127
|
"testMatch": [
|
|
119
128
|
"**/tests/unit/**/*.[jt]s?(x)",
|