react-native-onyx 2.0.94 → 2.0.96
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 +17 -2
- package/dist/Logger.d.ts +5 -3
- package/dist/Logger.js +6 -6
- package/dist/OnyxConnectionManager.js +10 -3
- package/dist/OnyxUtils.d.ts +1 -1
- package/dist/OnyxUtils.js +3 -3
- package/dist/types.d.ts +1 -1
- package/dist/useOnyx.d.ts +7 -0
- package/dist/useOnyx.js +22 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -256,6 +256,21 @@ DO NOT use more than one `withOnyx` component at a time. It adds overhead and pr
|
|
|
256
256
|
|
|
257
257
|
It's also beneficial to use a [selector](https://github.com/Expensify/react-native-onyx/blob/main/API.md#connectmapping--number) with the mapping in case you need to grab a single item in a collection (like a single report action).
|
|
258
258
|
|
|
259
|
+
### useOnyx()'s `canBeMissing` option
|
|
260
|
+
|
|
261
|
+
You must pass the `canBeMissing` configuration flag to `useOnyx` if you want the hook to log an alert when data is missing from Onyx store. Regarding usage in `Expensify/App` repo, if the component calling this is the one loading the data by calling an action, then you should set this to `true`. If the component calling this does not load the data then you should set it to `false`, which means that if the data is not there, it will log an alert, as it means we are using data that no one loaded and that's most probably a bug.
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
const Component = ({reportID}) => {
|
|
265
|
+
// This hook will log an alert (via `Logger.logAlert()`) if `report` is `undefined`.
|
|
266
|
+
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: false});
|
|
267
|
+
|
|
268
|
+
// rest of the component's code.
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export default Component;
|
|
272
|
+
```
|
|
273
|
+
|
|
259
274
|
## Collections
|
|
260
275
|
|
|
261
276
|
Collections allow keys with similar value types to be subscribed together by subscribing to the collection key. To define one, it must be included in the `ONYXKEYS.COLLECTION` object and it must be suffixed with an underscore. Member keys should use a unique identifier or index after the collection key prefix (e.g. `report_42`).
|
|
@@ -316,11 +331,11 @@ This will fire the callback once per member key depending on how many collection
|
|
|
316
331
|
Onyx.connect({
|
|
317
332
|
key: ONYXKEYS.COLLECTION.REPORT,
|
|
318
333
|
waitForCollectionCallback: true,
|
|
319
|
-
callback: (allReports) => {...},
|
|
334
|
+
callback: (allReports, collectionKey, sourceValue) => {...},
|
|
320
335
|
});
|
|
321
336
|
```
|
|
322
337
|
|
|
323
|
-
This final option forces `Onyx.connect()` to behave more like `useOnyx()` and only update the callback once with the entire collection initially and later with an updated version of the collection when individual keys update.
|
|
338
|
+
This final option forces `Onyx.connect()` to behave more like `useOnyx()` and only update the callback once with the entire collection initially and later with an updated version of the collection when individual keys update. The `sourceValue` parameter contains only the specific keys and values that triggered the current update, which can be useful for optimizing your code to only process what changed. This parameter is not available when `waitForCollectionCallback` is false or the key is not a collection key.
|
|
324
339
|
|
|
325
340
|
### Performance Considerations When Using Collections
|
|
326
341
|
|
package/dist/Logger.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
type Parameters = string | Record<string, unknown> | Array<Record<string, unknown>> | Error;
|
|
1
2
|
type LogData = {
|
|
2
3
|
message: string;
|
|
3
4
|
level: 'alert' | 'info' | 'hmmm';
|
|
5
|
+
parameters?: Parameters;
|
|
4
6
|
};
|
|
5
7
|
type LoggerCallback = (data: LogData) => void;
|
|
6
8
|
/**
|
|
@@ -10,13 +12,13 @@ declare function registerLogger(callback: LoggerCallback): void;
|
|
|
10
12
|
/**
|
|
11
13
|
* Send an alert message to the logger
|
|
12
14
|
*/
|
|
13
|
-
declare function logAlert(message: string): void;
|
|
15
|
+
declare function logAlert(message: string, parameters?: Parameters): void;
|
|
14
16
|
/**
|
|
15
17
|
* Send an info message to the logger
|
|
16
18
|
*/
|
|
17
|
-
declare function logInfo(message: string): void;
|
|
19
|
+
declare function logInfo(message: string, parameters?: Parameters): void;
|
|
18
20
|
/**
|
|
19
21
|
* Send an hmmm message to the logger
|
|
20
22
|
*/
|
|
21
|
-
declare function logHmmm(message: string): void;
|
|
23
|
+
declare function logHmmm(message: string, parameters?: Parameters): void;
|
|
22
24
|
export { registerLogger, logInfo, logAlert, logHmmm };
|
package/dist/Logger.js
CHANGED
|
@@ -13,21 +13,21 @@ exports.registerLogger = registerLogger;
|
|
|
13
13
|
/**
|
|
14
14
|
* Send an alert message to the logger
|
|
15
15
|
*/
|
|
16
|
-
function logAlert(message) {
|
|
17
|
-
logger({ message: `[Onyx] ${message}`, level: 'alert' });
|
|
16
|
+
function logAlert(message, parameters) {
|
|
17
|
+
logger({ message: `[Onyx] ${message}`, level: 'alert', parameters });
|
|
18
18
|
}
|
|
19
19
|
exports.logAlert = logAlert;
|
|
20
20
|
/**
|
|
21
21
|
* Send an info message to the logger
|
|
22
22
|
*/
|
|
23
|
-
function logInfo(message) {
|
|
24
|
-
logger({ message: `[Onyx] ${message}`, level: 'info' });
|
|
23
|
+
function logInfo(message, parameters) {
|
|
24
|
+
logger({ message: `[Onyx] ${message}`, level: 'info', parameters });
|
|
25
25
|
}
|
|
26
26
|
exports.logInfo = logInfo;
|
|
27
27
|
/**
|
|
28
28
|
* Send an hmmm message to the logger
|
|
29
29
|
*/
|
|
30
|
-
function logHmmm(message) {
|
|
31
|
-
logger({ message: `[Onyx] ${message}`, level: 'hmmm' });
|
|
30
|
+
function logHmmm(message, parameters) {
|
|
31
|
+
logger({ message: `[Onyx] ${message}`, level: 'hmmm', parameters });
|
|
32
32
|
}
|
|
33
33
|
exports.logHmmm = logHmmm;
|
|
@@ -73,7 +73,12 @@ class OnyxConnectionManager {
|
|
|
73
73
|
fireCallbacks(connectionID) {
|
|
74
74
|
const connection = this.connectionsMap.get(connectionID);
|
|
75
75
|
connection === null || connection === void 0 ? void 0 : connection.callbacks.forEach((callback) => {
|
|
76
|
-
|
|
76
|
+
if (connection.waitForCollectionCallback) {
|
|
77
|
+
callback(connection.cachedCallbackValue, connection.cachedCallbackKey, connection.sourceValue);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
callback(connection.cachedCallbackValue, connection.cachedCallbackKey);
|
|
81
|
+
}
|
|
77
82
|
});
|
|
78
83
|
}
|
|
79
84
|
/**
|
|
@@ -93,7 +98,7 @@ class OnyxConnectionManager {
|
|
|
93
98
|
// If the subscriber is a `withOnyx` HOC we don't define `callback` as the HOC will use
|
|
94
99
|
// its own logic to handle the data.
|
|
95
100
|
if (!utils_1.default.hasWithOnyxInstance(connectOptions)) {
|
|
96
|
-
callback = (value, key) => {
|
|
101
|
+
callback = (value, key, sourceValue) => {
|
|
97
102
|
const createdConnection = this.connectionsMap.get(connectionID);
|
|
98
103
|
if (createdConnection) {
|
|
99
104
|
// We signal that the first connection was made and now any new subscribers
|
|
@@ -101,16 +106,18 @@ class OnyxConnectionManager {
|
|
|
101
106
|
createdConnection.isConnectionMade = true;
|
|
102
107
|
createdConnection.cachedCallbackValue = value;
|
|
103
108
|
createdConnection.cachedCallbackKey = key;
|
|
109
|
+
createdConnection.sourceValue = sourceValue;
|
|
104
110
|
this.fireCallbacks(connectionID);
|
|
105
111
|
}
|
|
106
112
|
};
|
|
107
113
|
}
|
|
108
|
-
subscriptionID = OnyxUtils_1.default.subscribeToKey(Object.assign(Object.assign({}, connectOptions), { callback }));
|
|
114
|
+
subscriptionID = OnyxUtils_1.default.subscribeToKey(Object.assign(Object.assign({}, connectOptions), { callback: callback }));
|
|
109
115
|
connectionMetadata = {
|
|
110
116
|
subscriptionID,
|
|
111
117
|
onyxKey: connectOptions.key,
|
|
112
118
|
isConnectionMade: false,
|
|
113
119
|
callbacks: new Map(),
|
|
120
|
+
waitForCollectionCallback: connectOptions.waitForCollectionCallback,
|
|
114
121
|
};
|
|
115
122
|
this.connectionsMap.set(connectionID, connectionMetadata);
|
|
116
123
|
}
|
package/dist/OnyxUtils.d.ts
CHANGED
|
@@ -73,7 +73,7 @@ declare function multiGet<TKey extends OnyxKey>(keys: CollectionKeyBase[]): Prom
|
|
|
73
73
|
* This helper exists to map an array of Onyx keys such as `['report_', 'conciergeReportID']`
|
|
74
74
|
* to the values for those keys (correctly typed) such as `[OnyxCollection<Report>, OnyxEntry<string>]`
|
|
75
75
|
*
|
|
76
|
-
* Note: just using
|
|
76
|
+
* Note: just using `.map`, you'd end up with `Array<OnyxCollection<Report>|OnyxEntry<string>>`, which is not what we want. This preserves the order of the keys provided.
|
|
77
77
|
*/
|
|
78
78
|
declare function tupleGet<Keys extends readonly OnyxKey[]>(keys: Keys): Promise<{
|
|
79
79
|
[Index in keyof Keys]: OnyxValue<Keys[Index]>;
|
package/dist/OnyxUtils.js
CHANGED
|
@@ -314,7 +314,7 @@ function multiGet(keys) {
|
|
|
314
314
|
* This helper exists to map an array of Onyx keys such as `['report_', 'conciergeReportID']`
|
|
315
315
|
* to the values for those keys (correctly typed) such as `[OnyxCollection<Report>, OnyxEntry<string>]`
|
|
316
316
|
*
|
|
317
|
-
* Note: just using
|
|
317
|
+
* Note: just using `.map`, you'd end up with `Array<OnyxCollection<Report>|OnyxEntry<string>>`, which is not what we want. This preserves the order of the keys provided.
|
|
318
318
|
*/
|
|
319
319
|
function tupleGet(keys) {
|
|
320
320
|
return Promise.all(keys.map((key) => OnyxUtils.get(key)));
|
|
@@ -562,7 +562,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
|
|
|
562
562
|
// send the whole cached collection.
|
|
563
563
|
if (isSubscribedToCollectionKey) {
|
|
564
564
|
if (subscriber.waitForCollectionCallback) {
|
|
565
|
-
subscriber.callback(cachedCollection, subscriber.key);
|
|
565
|
+
subscriber.callback(cachedCollection, subscriber.key, partialCollection);
|
|
566
566
|
continue;
|
|
567
567
|
}
|
|
568
568
|
// If they are not using waitForCollectionCallback then we notify the subscriber with
|
|
@@ -738,7 +738,7 @@ function keyChanged(key, value, previousValue, canUpdateSubscriber = () => true,
|
|
|
738
738
|
cachedCollections[subscriber.key] = cachedCollection;
|
|
739
739
|
}
|
|
740
740
|
cachedCollection[key] = value;
|
|
741
|
-
subscriber.callback(cachedCollection, subscriber.key);
|
|
741
|
+
subscriber.callback(cachedCollection, subscriber.key, { [key]: value });
|
|
742
742
|
continue;
|
|
743
743
|
}
|
|
744
744
|
const subscriberCallback = subscriber.callback;
|
package/dist/types.d.ts
CHANGED
|
@@ -238,7 +238,7 @@ type BaseConnectOptions = {
|
|
|
238
238
|
/** Represents the callback function used in `Onyx.connect()` method with a regular key. */
|
|
239
239
|
type DefaultConnectCallback<TKey extends OnyxKey> = (value: OnyxEntry<KeyValueMapping[TKey]>, key: TKey) => void;
|
|
240
240
|
/** Represents the callback function used in `Onyx.connect()` method with a collection key. */
|
|
241
|
-
type CollectionConnectCallback<TKey extends OnyxKey> = (value: NonUndefined<OnyxCollection<KeyValueMapping[TKey]>>, key: TKey) => void;
|
|
241
|
+
type CollectionConnectCallback<TKey extends OnyxKey> = (value: NonUndefined<OnyxCollection<KeyValueMapping[TKey]>>, key: TKey, sourceValue?: OnyxValue<TKey>) => void;
|
|
242
242
|
/** Represents the options used in `Onyx.connect()` method with a regular key. */
|
|
243
243
|
type DefaultConnectOptions<TKey extends OnyxKey> = BaseConnectOptions & {
|
|
244
244
|
/** The Onyx key to subscribe to. */
|
package/dist/useOnyx.d.ts
CHANGED
|
@@ -22,6 +22,13 @@ type BaseUseOnyxOptions = {
|
|
|
22
22
|
* If set to `true`, the key can be changed dynamically during the component lifecycle.
|
|
23
23
|
*/
|
|
24
24
|
allowDynamicKey?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* If the component calling this is the one loading the data by calling an action, then you should set this to `true`.
|
|
27
|
+
*
|
|
28
|
+
* If the component calling this does not load the data then you should set it to `false`, which means that if the data
|
|
29
|
+
* is not there, it will log an alert, as it means we are using data that no one loaded and that's most probably a bug.
|
|
30
|
+
*/
|
|
31
|
+
canBeMissing?: boolean;
|
|
25
32
|
};
|
|
26
33
|
type UseOnyxInitialValueOption<TInitialValue> = {
|
|
27
34
|
/**
|
package/dist/useOnyx.js
CHANGED
|
@@ -35,6 +35,7 @@ const GlobalSettings = __importStar(require("./GlobalSettings"));
|
|
|
35
35
|
const useLiveRef_1 = __importDefault(require("./useLiveRef"));
|
|
36
36
|
const usePrevious_1 = __importDefault(require("./usePrevious"));
|
|
37
37
|
const metrics_1 = __importDefault(require("./metrics"));
|
|
38
|
+
const Logger = __importStar(require("./Logger"));
|
|
38
39
|
/**
|
|
39
40
|
* Gets the cached value from the Onyx cache. If the key is a collection key, it will return all the values in the collection.
|
|
40
41
|
* It is a fork of `tryGetCachedValue` from `OnyxUtils` caused by different selector logic in `useOnyx`. It should be unified in the future, when `withOnyx` is removed.
|
|
@@ -58,14 +59,6 @@ function tryGetCachedValue(key) {
|
|
|
58
59
|
});
|
|
59
60
|
return values;
|
|
60
61
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Gets the value from cache and maps it with selector. It changes `null` to `undefined` for `useOnyx` compatibility.
|
|
63
|
-
*/
|
|
64
|
-
function getCachedValue(key, selector) {
|
|
65
|
-
const value = tryGetCachedValue(key);
|
|
66
|
-
const selectedValue = selector ? selector(value) : value;
|
|
67
|
-
return selectedValue !== null && selectedValue !== void 0 ? selectedValue : undefined;
|
|
68
|
-
}
|
|
69
62
|
function useOnyx(key, options, dependencies = []) {
|
|
70
63
|
const connectionRef = (0, react_1.useRef)(null);
|
|
71
64
|
const previousKey = (0, usePrevious_1.default)(key);
|
|
@@ -140,7 +133,8 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
140
133
|
}
|
|
141
134
|
}, [key, options === null || options === void 0 ? void 0 : options.canEvict]);
|
|
142
135
|
const getSnapshot = (0, react_1.useCallback)(() => {
|
|
143
|
-
var _a, _b, _c
|
|
136
|
+
var _a, _b, _c;
|
|
137
|
+
let isOnyxValueDefined = true;
|
|
144
138
|
// We return the initial result right away during the first connection if `initWithStoredValues` is set to `false`.
|
|
145
139
|
if (isFirstConnectionRef.current && (options === null || options === void 0 ? void 0 : options.initWithStoredValues) === false) {
|
|
146
140
|
return resultRef.current;
|
|
@@ -149,11 +143,13 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
149
143
|
// so we can return any cached value right away. After the connection is made, we only
|
|
150
144
|
// update `newValueRef` when `Onyx.connect()` callback is fired.
|
|
151
145
|
if (isFirstConnectionRef.current || shouldGetCachedValueRef.current) {
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
// Gets the value from cache and maps it with selector. It changes `null` to `undefined` for `useOnyx` compatibility.
|
|
147
|
+
const value = tryGetCachedValue(key);
|
|
148
|
+
const selectedValue = selectorRef.current ? selectorRef.current(value) : value;
|
|
149
|
+
newValueRef.current = (selectedValue !== null && selectedValue !== void 0 ? selectedValue : undefined);
|
|
150
|
+
// This flag is `false` when the original Onyx value (without selector) is not defined yet.
|
|
151
|
+
// It will be used later to check if we need to log an alert that the value is missing.
|
|
152
|
+
isOnyxValueDefined = value !== null && value !== undefined;
|
|
157
153
|
// We set this flag to `false` again since we don't want to get the newest cached value every time `getSnapshot()` is executed,
|
|
158
154
|
// and only when `Onyx.connect()` callback is fired.
|
|
159
155
|
shouldGetCachedValueRef.current = false;
|
|
@@ -171,7 +167,7 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
171
167
|
// If data is not present in cache and `initialValue` is set during the first connection,
|
|
172
168
|
// we set the new value to `initialValue` and fetch status to `loaded` since we already have some data to return to the consumer.
|
|
173
169
|
if (isFirstConnectionRef.current && !hasCacheForKey && (options === null || options === void 0 ? void 0 : options.initialValue) !== undefined) {
|
|
174
|
-
newValueRef.current =
|
|
170
|
+
newValueRef.current = options.initialValue;
|
|
175
171
|
newFetchStatus = 'loaded';
|
|
176
172
|
}
|
|
177
173
|
// We do a deep equality check if `selector` is defined, since each `tryGetCachedValue()` call will
|
|
@@ -179,12 +175,12 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
179
175
|
// For the other cases we will only deal with object reference checks, so just a shallow equality check is enough.
|
|
180
176
|
let areValuesEqual;
|
|
181
177
|
if (selectorRef.current) {
|
|
182
|
-
areValuesEqual = (0, fast_equals_1.deepEqual)((
|
|
178
|
+
areValuesEqual = (0, fast_equals_1.deepEqual)((_a = previousValueRef.current) !== null && _a !== void 0 ? _a : undefined, newValueRef.current);
|
|
183
179
|
}
|
|
184
180
|
else {
|
|
185
|
-
areValuesEqual = (0, fast_equals_1.shallowEqual)((
|
|
181
|
+
areValuesEqual = (0, fast_equals_1.shallowEqual)((_b = previousValueRef.current) !== null && _b !== void 0 ? _b : undefined, newValueRef.current);
|
|
186
182
|
}
|
|
187
|
-
// We
|
|
183
|
+
// We update the cached value and the result in the following conditions:
|
|
188
184
|
// We will update the cached value and the result in any of the following situations:
|
|
189
185
|
// - The previously cached value is different from the new value.
|
|
190
186
|
// - The previously cached value is `null` (not set from cache yet) and we have cache for this key
|
|
@@ -194,10 +190,16 @@ function useOnyx(key, options, dependencies = []) {
|
|
|
194
190
|
if (shouldUpdateResult) {
|
|
195
191
|
previousValueRef.current = newValueRef.current;
|
|
196
192
|
// If the new value is `null` we default it to `undefined` to ensure the consumer gets a consistent result from the hook.
|
|
197
|
-
|
|
193
|
+
const newStatus = newFetchStatus !== null && newFetchStatus !== void 0 ? newFetchStatus : 'loaded';
|
|
194
|
+
resultRef.current = [(_c = previousValueRef.current) !== null && _c !== void 0 ? _c : undefined, { status: newStatus }];
|
|
195
|
+
// If `canBeMissing` is set to `false` and the Onyx value of that key is not defined,
|
|
196
|
+
// we log an alert so it can be acknowledged by the consumer.
|
|
197
|
+
if ((options === null || options === void 0 ? void 0 : options.canBeMissing) === false && newStatus === 'loaded' && !isOnyxValueDefined) {
|
|
198
|
+
Logger.logAlert(`useOnyx returned no data for key with canBeMissing set to false.`, { key, showAlert: true });
|
|
199
|
+
}
|
|
198
200
|
}
|
|
199
201
|
return resultRef.current;
|
|
200
|
-
}, [options === null || options === void 0 ? void 0 : options.initWithStoredValues, options === null || options === void 0 ? void 0 : options.allowStaleData, options === null || options === void 0 ? void 0 : options.initialValue, key, selectorRef]);
|
|
202
|
+
}, [options === null || options === void 0 ? void 0 : options.initWithStoredValues, options === null || options === void 0 ? void 0 : options.allowStaleData, options === null || options === void 0 ? void 0 : options.initialValue, options === null || options === void 0 ? void 0 : options.canBeMissing, key, selectorRef]);
|
|
201
203
|
const subscribe = (0, react_1.useCallback)((onStoreChange) => {
|
|
202
204
|
isConnectingRef.current = true;
|
|
203
205
|
onStoreChangeFnRef.current = onStoreChange;
|