react-native-onyx 2.0.31 → 2.0.33

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/Onyx.js CHANGED
@@ -221,7 +221,7 @@ function set(key, value) {
221
221
  * @param data object keyed by ONYXKEYS and the values to set
222
222
  */
223
223
  function multiSet(data) {
224
- const keyValuePairs = OnyxUtils_1.default.prepareKeyValuePairsForStorage(data);
224
+ const keyValuePairs = OnyxUtils_1.default.prepareKeyValuePairsForStorage(data, true);
225
225
  const updatePromises = keyValuePairs.map(([key, value]) => {
226
226
  const prevValue = OnyxCache_1.default.getValue(key, false);
227
227
  // Update cache and optimistically inform subscribers on the next tick
@@ -256,7 +256,7 @@ function merge(key, changes) {
256
256
  const mergeQueue = OnyxUtils_1.default.getMergeQueue();
257
257
  const mergeQueuePromise = OnyxUtils_1.default.getMergeQueuePromise();
258
258
  // Top-level undefined values are ignored
259
- // Therefore we need to prevent adding them to the merge queue
259
+ // Therefore, we need to prevent adding them to the merge queue
260
260
  if (changes === undefined) {
261
261
  return mergeQueue[key] ? mergeQueuePromise[key] : Promise.resolve();
262
262
  }
@@ -369,8 +369,13 @@ function mergeCollection(collectionKey, collection) {
369
369
  obj[key] = mergedCollection[key];
370
370
  return obj;
371
371
  }, {});
372
- const keyValuePairsForExistingCollection = OnyxUtils_1.default.prepareKeyValuePairsForStorage(existingKeyCollection);
373
- const keyValuePairsForNewCollection = OnyxUtils_1.default.prepareKeyValuePairsForStorage(newCollection);
372
+ // When (multi-)merging the values with the existing values in storage,
373
+ // we don't want to remove nested null values from the data that we pass to the storage layer,
374
+ // because the storage layer uses them to remove nested keys from storage natively.
375
+ const keyValuePairsForExistingCollection = OnyxUtils_1.default.prepareKeyValuePairsForStorage(existingKeyCollection, false);
376
+ // We can safely remove nested null values when using (multi-)set,
377
+ // because we will simply overwrite the existing values in storage.
378
+ const keyValuePairsForNewCollection = OnyxUtils_1.default.prepareKeyValuePairsForStorage(newCollection, true);
374
379
  const promises = [];
375
380
  // New keys will be added via multiSet while existing keys will be updated using multiMerge
376
381
  // This is because setting a key that doesn't exist yet with multiMerge will throw errors
@@ -165,11 +165,12 @@ type RemoveNullValuesOutput = {
165
165
  };
166
166
  /**
167
167
  * Removes a key from storage if the value is null.
168
- * Otherwise removes all nested null values in objects and returns the object
168
+ * Otherwise removes all nested null values in objects,
169
+ * if shouldRemoveNestedNulls is true and returns the object.
169
170
  *
170
171
  * @returns The value without null values and a boolean "wasRemoved", which indicates if the key got removed completely
171
172
  */
172
- declare function removeNullValues(key: OnyxKey, value: OnyxValue<OnyxKey>): RemoveNullValuesOutput;
173
+ declare function removeNullValues(key: OnyxKey, value: OnyxValue<OnyxKey>, shouldRemoveNestedNulls?: boolean): RemoveNullValuesOutput;
173
174
  /**
174
175
  * Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
175
176
  * This method transforms an object like {'@MyApp_user': myUserValue, '@MyApp_key': myKeyValue}
@@ -177,13 +178,13 @@ declare function removeNullValues(key: OnyxKey, value: OnyxValue<OnyxKey>): Remo
177
178
 
178
179
  * @return an array of key - value pairs <[key, value]>
179
180
  */
180
- declare function prepareKeyValuePairsForStorage(data: Record<OnyxKey, OnyxValue<OnyxKey>>): Array<[OnyxKey, OnyxValue<OnyxKey>]>;
181
+ declare function prepareKeyValuePairsForStorage(data: Record<OnyxKey, OnyxValue<OnyxKey>>, shouldRemoveNestedNulls: boolean): Array<[OnyxKey, OnyxValue<OnyxKey>]>;
181
182
  /**
182
183
  * Merges an array of changes with an existing value
183
184
  *
184
185
  * @param changes Array of changes that should be applied to the existing value
185
186
  */
186
- declare function applyMerge(existingValue: OnyxValue<OnyxKey>, changes: Array<OnyxValue<OnyxKey>>, shouldRemoveNullObjectValues: boolean): OnyxValue<OnyxKey>;
187
+ declare function applyMerge(existingValue: OnyxValue<OnyxKey>, changes: Array<OnyxValue<OnyxKey>>, shouldRemoveNestedNulls: boolean): OnyxValue<OnyxKey>;
187
188
  /**
188
189
  * Merge user provided default key value pairs.
189
190
  */
package/dist/OnyxUtils.js CHANGED
@@ -363,6 +363,7 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
363
363
  // We prepare the "cached collection" which is the entire collection + the new partial data that
364
364
  // was merged in via mergeCollection().
365
365
  const cachedCollection = getCachedCollection(collectionKey);
366
+ const cachedCollectionWithoutNestedNulls = utils_1.default.removeNestedNullValues(cachedCollection);
366
367
  // Regular Onyx.connect() subscriber found.
367
368
  if (typeof subscriber.callback === 'function') {
368
369
  if (!notifyRegularSubscibers) {
@@ -372,7 +373,7 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
372
373
  // send the whole cached collection.
373
374
  if (isSubscribedToCollectionKey) {
374
375
  if (subscriber.waitForCollectionCallback) {
375
- subscriber.callback(cachedCollection);
376
+ subscriber.callback(cachedCollectionWithoutNestedNulls);
376
377
  continue;
377
378
  }
378
379
  // If they are not using waitForCollectionCallback then we notify the subscriber with
@@ -380,7 +381,7 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
380
381
  const dataKeys = Object.keys(partialCollection !== null && partialCollection !== void 0 ? partialCollection : {});
381
382
  for (let j = 0; j < dataKeys.length; j++) {
382
383
  const dataKey = dataKeys[j];
383
- subscriber.callback(cachedCollection[dataKey], dataKey);
384
+ subscriber.callback(cachedCollectionWithoutNestedNulls[dataKey], dataKey);
384
385
  }
385
386
  continue;
386
387
  }
@@ -388,7 +389,7 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
388
389
  // notify them with the cached data for that key only.
389
390
  if (isSubscribedToCollectionMemberKey) {
390
391
  const subscriberCallback = subscriber.callback;
391
- subscriberCallback(cachedCollection[subscriber.key], subscriber.key);
392
+ subscriberCallback(cachedCollectionWithoutNestedNulls[subscriber.key], subscriber.key);
392
393
  continue;
393
394
  }
394
395
  continue;
@@ -507,12 +508,14 @@ function keyChanged(key, data, prevData, canUpdateSubscriber = () => true, notif
507
508
  }
508
509
  if (isCollectionKey(subscriber.key) && subscriber.waitForCollectionCallback) {
509
510
  const cachedCollection = getCachedCollection(subscriber.key);
510
- cachedCollection[key] = data;
511
- subscriber.callback(cachedCollection);
511
+ const cachedCollectionWithoutNestedNulls = utils_1.default.removeNestedNullValues(cachedCollection);
512
+ cachedCollectionWithoutNestedNulls[key] = data;
513
+ subscriber.callback(cachedCollectionWithoutNestedNulls);
512
514
  continue;
513
515
  }
516
+ const dataWithoutNestedNulls = utils_1.default.removeNestedNullValues(data);
514
517
  const subscriberCallback = subscriber.callback;
515
- subscriberCallback(data, key);
518
+ subscriberCallback(dataWithoutNestedNulls, key);
516
519
  continue;
517
520
  }
518
521
  // Subscriber connected via withOnyx() HOC
@@ -620,7 +623,8 @@ function sendDataToConnection(mapping, val, matchedKey, isBatched) {
620
623
  }
621
624
  return;
622
625
  }
623
- (_b = (_a = mapping).callback) === null || _b === void 0 ? void 0 : _b.call(_a, val, matchedKey);
626
+ const valuesWithoutNestedNulls = utils_1.default.removeNestedNullValues(val);
627
+ (_b = (_a = mapping).callback) === null || _b === void 0 ? void 0 : _b.call(_a, valuesWithoutNestedNulls, matchedKey);
624
628
  }
625
629
  /**
626
630
  * We check to see if this key is flagged as safe for eviction and add it to the recentlyAccessedKeys list so that when we
@@ -794,11 +798,12 @@ function hasPendingMergeForKey(key) {
794
798
  }
795
799
  /**
796
800
  * Removes a key from storage if the value is null.
797
- * Otherwise removes all nested null values in objects and returns the object
801
+ * Otherwise removes all nested null values in objects,
802
+ * if shouldRemoveNestedNulls is true and returns the object.
798
803
  *
799
804
  * @returns The value without null values and a boolean "wasRemoved", which indicates if the key got removed completely
800
805
  */
801
- function removeNullValues(key, value) {
806
+ function removeNullValues(key, value, shouldRemoveNestedNulls = true) {
802
807
  if (value === null) {
803
808
  remove(key);
804
809
  return { value, wasRemoved: true };
@@ -806,7 +811,7 @@ function removeNullValues(key, value) {
806
811
  // We can remove all null values in an object by merging it with itself
807
812
  // utils.fastMerge recursively goes through the object and removes all null values
808
813
  // Passing two identical objects as source and target to fastMerge will not change it, but only remove the null values
809
- return { value: utils_1.default.removeNestedNullValues(value), wasRemoved: false };
814
+ return { value: shouldRemoveNestedNulls ? utils_1.default.removeNestedNullValues(value) : value, wasRemoved: false };
810
815
  }
811
816
  /**
812
817
  * Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
@@ -815,30 +820,28 @@ function removeNullValues(key, value) {
815
820
 
816
821
  * @return an array of key - value pairs <[key, value]>
817
822
  */
818
- function prepareKeyValuePairsForStorage(data) {
819
- const keyValuePairs = [];
820
- Object.entries(data).forEach(([key, value]) => {
821
- const { value: valueAfterRemoving, wasRemoved } = removeNullValues(key, value);
822
- if (wasRemoved) {
823
- return;
823
+ function prepareKeyValuePairsForStorage(data, shouldRemoveNestedNulls) {
824
+ return Object.entries(data).reduce((pairs, [key, value]) => {
825
+ const { value: valueAfterRemoving, wasRemoved } = removeNullValues(key, value, shouldRemoveNestedNulls);
826
+ if (!wasRemoved) {
827
+ pairs.push([key, valueAfterRemoving]);
824
828
  }
825
- keyValuePairs.push([key, valueAfterRemoving]);
826
- });
827
- return keyValuePairs;
829
+ return pairs;
830
+ }, []);
828
831
  }
829
832
  /**
830
833
  * Merges an array of changes with an existing value
831
834
  *
832
835
  * @param changes Array of changes that should be applied to the existing value
833
836
  */
834
- function applyMerge(existingValue, changes, shouldRemoveNullObjectValues) {
837
+ function applyMerge(existingValue, changes, shouldRemoveNestedNulls) {
835
838
  const lastChange = changes === null || changes === void 0 ? void 0 : changes.at(-1);
836
839
  if (Array.isArray(lastChange)) {
837
840
  return lastChange;
838
841
  }
839
842
  if (changes.some((change) => change && typeof change === 'object')) {
840
843
  // Object values are then merged one after the other
841
- return changes.reduce((modifiedData, change) => utils_1.default.fastMerge(modifiedData, change, shouldRemoveNullObjectValues), existingValue || {});
844
+ return changes.reduce((modifiedData, change) => utils_1.default.fastMerge(modifiedData, change, shouldRemoveNestedNulls), existingValue || {});
842
845
  }
843
846
  // If we have anything else we can't merge it so we'll
844
847
  // simply return the last value that was queued
@@ -55,11 +55,7 @@ const provider = {
55
55
  */
56
56
  multiSet(pairs) {
57
57
  const setPromises = underscore_1.default.map(pairs, ([key, value]) => this.setItem(key, value));
58
- return new Promise((resolve) => {
59
- Promise.all(setPromises).then(() => {
60
- resolve(undefined);
61
- });
62
- });
58
+ return Promise.all(setPromises).then(() => undefined);
63
59
  },
64
60
  /**
65
61
  * Merging an existing value with a new one
package/dist/utils.d.ts CHANGED
@@ -4,15 +4,15 @@ type EmptyValue = EmptyObject | null | undefined;
4
4
  /** Checks whether the given object is an object and not null/undefined. */
5
5
  declare function isEmptyObject<T>(obj: T | EmptyValue): obj is EmptyValue;
6
6
  /**
7
- * Merges two objects and removes null values if "shouldRemoveNullObjectValues" is set to true
7
+ * Merges two objects and removes null values if "shouldRemoveNestedNulls" is set to true
8
8
  *
9
9
  * We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk.
10
10
  * On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values.
11
11
  * To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations.
12
12
  */
13
- declare function fastMerge<TObject extends Record<string, unknown>>(target: TObject | null, source: TObject | null, shouldRemoveNullObjectValues?: boolean): TObject | null;
13
+ declare function fastMerge<TObject extends Record<string, unknown>>(target: TObject | null, source: TObject | null, shouldRemoveNestedNulls?: boolean): TObject | null;
14
14
  /** Deep removes the nested null values from the given value. */
15
- declare function removeNestedNullValues(value: unknown[] | Record<string, unknown>): Record<string, unknown> | unknown[] | null;
15
+ declare function removeNestedNullValues<TObject extends Record<string, unknown>>(value: unknown | unknown[] | TObject): Record<string, unknown> | unknown[] | null;
16
16
  /** Formats the action name by uppercasing and adding the key if provided. */
17
17
  declare function formatActionName(method: string, key?: OnyxKey): string;
18
18
  declare const _default: {
package/dist/utils.js CHANGED
@@ -17,14 +17,14 @@ function isMergeableObject(value) {
17
17
  * Merges the source object into the target object.
18
18
  * @param target - The target object.
19
19
  * @param source - The source object.
20
- * @param shouldRemoveNullObjectValues - If true, null object values will be removed.
20
+ * @param shouldRemoveNestedNulls - If true, null object values will be removed.
21
21
  * @returns - The merged object.
22
22
  */
23
- function mergeObject(target, source, shouldRemoveNullObjectValues = true) {
23
+ function mergeObject(target, source, shouldRemoveNestedNulls = true) {
24
24
  const destination = {};
25
25
  // First we want to copy over all keys from the target into the destination object,
26
26
  // in case "target" is a mergable object.
27
- // If "shouldRemoveNullObjectValues" is true, we want to remove null values from the merged object
27
+ // If "shouldRemoveNestedNulls" is true, we want to remove null values from the merged object
28
28
  // and therefore we need to omit keys where either the source or target value is null.
29
29
  if (isMergeableObject(target)) {
30
30
  const targetKeys = Object.keys(target);
@@ -32,10 +32,10 @@ function mergeObject(target, source, shouldRemoveNullObjectValues = true) {
32
32
  const key = targetKeys[i];
33
33
  const sourceValue = source === null || source === void 0 ? void 0 : source[key];
34
34
  const targetValue = target === null || target === void 0 ? void 0 : target[key];
35
- // If "shouldRemoveNullObjectValues" is true, we want to remove null values from the merged object.
35
+ // If "shouldRemoveNestedNulls" is true, we want to remove null values from the merged object.
36
36
  // Therefore, if either target or source value is null, we want to prevent the key from being set.
37
37
  const isSourceOrTargetNull = targetValue === null || sourceValue === null;
38
- const shouldOmitTargetKey = shouldRemoveNullObjectValues && isSourceOrTargetNull;
38
+ const shouldOmitTargetKey = shouldRemoveNestedNulls && isSourceOrTargetNull;
39
39
  if (!shouldOmitTargetKey) {
40
40
  destination[key] = targetValue;
41
41
  }
@@ -48,13 +48,13 @@ function mergeObject(target, source, shouldRemoveNullObjectValues = true) {
48
48
  const sourceValue = source === null || source === void 0 ? void 0 : source[key];
49
49
  const targetValue = target === null || target === void 0 ? void 0 : target[key];
50
50
  // If undefined is passed as the source value for a key, we want to generally ignore it.
51
- // If "shouldRemoveNullObjectValues" is set to true and the source value is null,
51
+ // If "shouldRemoveNestedNulls" is set to true and the source value is null,
52
52
  // we don't want to set/merge the source value into the merged object.
53
- const shouldIgnoreNullSourceValue = shouldRemoveNullObjectValues && sourceValue === null;
53
+ const shouldIgnoreNullSourceValue = shouldRemoveNestedNulls && sourceValue === null;
54
54
  const shouldOmitSourceKey = sourceValue === undefined || shouldIgnoreNullSourceValue;
55
55
  if (!shouldOmitSourceKey) {
56
56
  // If the source value is a mergable object, we want to merge it into the target value.
57
- // If "shouldRemoveNullObjectValues" is true, "fastMerge" will recursively
57
+ // If "shouldRemoveNestedNulls" is true, "fastMerge" will recursively
58
58
  // remove nested null values from the merged object.
59
59
  // If source value is any other value we need to set the source value it directly.
60
60
  if (isMergeableObject(sourceValue)) {
@@ -62,7 +62,7 @@ function mergeObject(target, source, shouldRemoveNullObjectValues = true) {
62
62
  // so that we can still use "fastMerge" to merge the source value,
63
63
  // to ensure that nested null values are removed from the merged object.
64
64
  const targetValueWithFallback = (targetValue !== null && targetValue !== void 0 ? targetValue : {});
65
- destination[key] = fastMerge(targetValueWithFallback, sourceValue, shouldRemoveNullObjectValues);
65
+ destination[key] = fastMerge(targetValueWithFallback, sourceValue, shouldRemoveNestedNulls);
66
66
  }
67
67
  else {
68
68
  destination[key] = sourceValue;
@@ -72,20 +72,20 @@ function mergeObject(target, source, shouldRemoveNullObjectValues = true) {
72
72
  return destination;
73
73
  }
74
74
  /**
75
- * Merges two objects and removes null values if "shouldRemoveNullObjectValues" is set to true
75
+ * Merges two objects and removes null values if "shouldRemoveNestedNulls" is set to true
76
76
  *
77
77
  * We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk.
78
78
  * On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values.
79
79
  * To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations.
80
80
  */
81
- function fastMerge(target, source, shouldRemoveNullObjectValues = true) {
81
+ function fastMerge(target, source, shouldRemoveNestedNulls = true) {
82
82
  // We have to ignore arrays and nullish values here,
83
83
  // otherwise "mergeObject" will throw an error,
84
84
  // because it expects an object as "source"
85
85
  if (Array.isArray(source) || source === null || source === undefined) {
86
86
  return source;
87
87
  }
88
- return mergeObject(target, source, shouldRemoveNullObjectValues);
88
+ return mergeObject(target, source, shouldRemoveNestedNulls);
89
89
  }
90
90
  /** Deep removes the nested null values from the given value. */
91
91
  function removeNestedNullValues(value) {
package/dist/withOnyx.js CHANGED
@@ -298,8 +298,9 @@ function default_1(mapOnyxToState, shouldDelayUpdates = false) {
298
298
  // that should not be passed to a wrapped component
299
299
  let stateToPass = underscore_1.default.omit(this.state, 'loading');
300
300
  stateToPass = underscore_1.default.omit(stateToPass, underscore_1.default.isNull);
301
+ const stateToPassWithoutNestedNulls = utils_1.default.removeNestedNullValues(stateToPass);
301
302
  // Spreading props and state is necessary in an HOC where the data cannot be predicted
302
- return (react_1.default.createElement(WrappedComponent, Object.assign({ markReadyForHydration: this.flushPendingSetStates }, propsToPass, stateToPass, { ref: this.props.forwardedRef })));
303
+ return (react_1.default.createElement(WrappedComponent, Object.assign({ markReadyForHydration: this.flushPendingSetStates }, propsToPass, stateToPassWithoutNestedNulls, { ref: this.props.forwardedRef })));
303
304
  }
304
305
  }
305
306
  withOnyx.propTypes = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "2.0.31",
3
+ "version": "2.0.33",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",