react-native-onyx 2.0.93 → 2.0.95

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 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`).
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;
package/dist/Onyx.js CHANGED
@@ -569,6 +569,10 @@ function updateSnapshots(data) {
569
569
  updatedData[key] = value || [];
570
570
  return;
571
571
  }
572
+ if (value === null) {
573
+ updatedData[key] = value;
574
+ return;
575
+ }
572
576
  const oldValue = updatedData[key] || {};
573
577
  const newValue = (0, pick_1.default)(value, Object.keys(snapshotData[key]));
574
578
  updatedData = Object.assign(Object.assign({}, updatedData), { [key]: Object.assign(oldValue, newValue) });
@@ -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 .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.
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 .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.
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)));
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, _d;
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
- // If `newValueRef.current` is `undefined` it means that the cache doesn't have a value for that key yet.
153
- // If `newValueRef.current` is `null` or any other value it means that the cache does have a value for that key.
154
- // This difference between `undefined` and other values is crucial and it's used to address the following
155
- // conditions and use cases.
156
- newValueRef.current = getCachedValue(key, selectorRef.current);
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 = ((_a = options === null || options === void 0 ? void 0 : options.initialValue) !== null && _a !== void 0 ? _a : undefined);
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)((_b = previousValueRef.current) !== null && _b !== void 0 ? _b : undefined, newValueRef.current);
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)((_c = previousValueRef.current) !== null && _c !== void 0 ? _c : undefined, newValueRef.current);
181
+ areValuesEqual = (0, fast_equals_1.shallowEqual)((_b = previousValueRef.current) !== null && _b !== void 0 ? _b : undefined, newValueRef.current);
186
182
  }
187
- // We updated the cached value and the result in the following conditions:
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
- resultRef.current = [(_d = previousValueRef.current) !== null && _d !== void 0 ? _d : undefined, { status: newFetchStatus !== null && newFetchStatus !== void 0 ? newFetchStatus : 'loaded' }];
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "2.0.93",
3
+ "version": "2.0.95",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",