react-native-onyx 1.0.122 → 1.0.124
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/web.development.js +38 -42
- 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.native.js +1 -6
- package/lib/ActiveClientManager/index.web.js +3 -8
- package/lib/Onyx.d.ts +3 -3
- package/lib/Onyx.js +38 -42
- package/lib/broadcast/index.native.js +1 -3
- package/lib/broadcast/index.web.js +2 -4
- package/package.json +2 -2
|
@@ -63,14 +63,14 @@ function init() {
|
|
|
63
63
|
}
|
|
64
64
|
activeClientID = message.data.clientID;
|
|
65
65
|
|
|
66
|
-
subscribers.forEach(callback => callback());
|
|
66
|
+
subscribers.forEach((callback) => callback());
|
|
67
67
|
break;
|
|
68
68
|
}
|
|
69
69
|
case REMOVED_LEADER_MESSAGE:
|
|
70
70
|
activeClientID = clientID;
|
|
71
71
|
timestamp = Date.now();
|
|
72
72
|
Broadcast.sendMessage({type: NEW_LEADER_MESSAGE, clientID, timestamp});
|
|
73
|
-
subscribers.forEach(callback => callback());
|
|
73
|
+
subscribers.forEach((callback) => callback());
|
|
74
74
|
break;
|
|
75
75
|
default:
|
|
76
76
|
break;
|
|
@@ -91,9 +91,4 @@ function init() {
|
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
export {
|
|
95
|
-
isClientTheLeader,
|
|
96
|
-
init,
|
|
97
|
-
isReady,
|
|
98
|
-
subscribeToClientChange,
|
|
99
|
-
};
|
|
94
|
+
export {isClientTheLeader, init, isReady, subscribeToClientChange};
|
package/lib/Onyx.d.ts
CHANGED
|
@@ -315,9 +315,9 @@ declare const Onyx: {
|
|
|
315
315
|
METHOD: typeof METHOD;
|
|
316
316
|
setMemoryOnlyKeys: typeof setMemoryOnlyKeys;
|
|
317
317
|
onClear: typeof onClear;
|
|
318
|
-
isClientManagerReady: typeof ActiveClientManager.isReady
|
|
319
|
-
isClientTheLeader: typeof ActiveClientManager.isClientTheLeader
|
|
320
|
-
subscribeToClientChange: typeof ActiveClientManager.subscribeToClientChange
|
|
318
|
+
isClientManagerReady: typeof ActiveClientManager.isReady;
|
|
319
|
+
isClientTheLeader: typeof ActiveClientManager.isClientTheLeader;
|
|
320
|
+
subscribeToClientChange: typeof ActiveClientManager.subscribeToClientChange;
|
|
321
321
|
};
|
|
322
322
|
|
|
323
323
|
export default Onyx;
|
package/lib/Onyx.js
CHANGED
|
@@ -1021,17 +1021,18 @@ function evictStorageAndRetry(error, onyxMethod, ...args) {
|
|
|
1021
1021
|
*
|
|
1022
1022
|
* @param {String} key
|
|
1023
1023
|
* @param {*} value
|
|
1024
|
-
* @param {Boolean} hasChanged
|
|
1025
1024
|
* @param {String} method
|
|
1025
|
+
* @param {Boolean} hasChanged
|
|
1026
|
+
* @param {Boolean} wasRemoved
|
|
1026
1027
|
* @returns {Promise}
|
|
1027
1028
|
*/
|
|
1028
|
-
function broadcastUpdate(key, value, hasChanged,
|
|
1029
|
+
function broadcastUpdate(key, value, method, hasChanged, wasRemoved = false) {
|
|
1029
1030
|
// Logging properties only since values could be sensitive things we don't want to log
|
|
1030
1031
|
Logger.logInfo(`${method}() called for key: ${key}${_.isObject(value) ? ` properties: ${_.keys(value).join(',')}` : ''}`);
|
|
1031
1032
|
|
|
1032
1033
|
// Update subscribers if the cached value has changed, or when the subscriber specifically requires
|
|
1033
1034
|
// all updates regardless of value changes (indicated by initWithStoredValues set to false).
|
|
1034
|
-
if (hasChanged) {
|
|
1035
|
+
if (hasChanged && !wasRemoved) {
|
|
1035
1036
|
cache.set(key, value);
|
|
1036
1037
|
} else {
|
|
1037
1038
|
cache.addToAccessedKeys(key);
|
|
@@ -1053,18 +1054,18 @@ function hasPendingMergeForKey(key) {
|
|
|
1053
1054
|
* Otherwise removes all nested null values in objects and returns the object
|
|
1054
1055
|
* @param {String} key
|
|
1055
1056
|
* @param {Mixed} value
|
|
1056
|
-
* @returns {Mixed}
|
|
1057
|
+
* @returns {Mixed} The value without null values and a boolean "wasRemoved", which indicates if the key got removed completely
|
|
1057
1058
|
*/
|
|
1058
1059
|
function removeNullValues(key, value) {
|
|
1059
1060
|
if (_.isNull(value)) {
|
|
1060
1061
|
remove(key);
|
|
1061
|
-
return
|
|
1062
|
+
return {value, wasRemoved: true};
|
|
1062
1063
|
}
|
|
1063
1064
|
|
|
1064
1065
|
// We can remove all null values in an object by merging it with itself
|
|
1065
1066
|
// utils.fastMerge recursively goes through the object and removes all null values
|
|
1066
1067
|
// Passing two identical objects as source and target to fastMerge will not change it, but only remove the null values
|
|
1067
|
-
return utils.removeNestedNullValues(value);
|
|
1068
|
+
return {value: utils.removeNestedNullValues(value), wasRemoved: false};
|
|
1068
1069
|
}
|
|
1069
1070
|
|
|
1070
1071
|
/**
|
|
@@ -1085,41 +1086,48 @@ function set(key, value) {
|
|
|
1085
1086
|
return Promise.resolve();
|
|
1086
1087
|
}
|
|
1087
1088
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
if (valueWithoutNull === null) {
|
|
1091
|
-
return Promise.resolve();
|
|
1092
|
-
}
|
|
1089
|
+
// If the value is null, we remove the key from storage
|
|
1090
|
+
const {value: valueAfterRemoving, wasRemoved} = removeNullValues(key, value);
|
|
1093
1091
|
|
|
1094
1092
|
if (hasPendingMergeForKey(key)) {
|
|
1095
|
-
|
|
1093
|
+
delete mergeQueue[key];
|
|
1096
1094
|
}
|
|
1097
1095
|
|
|
1098
|
-
const hasChanged = cache.hasValueChanged(key,
|
|
1096
|
+
const hasChanged = cache.hasValueChanged(key, valueAfterRemoving);
|
|
1099
1097
|
|
|
1100
1098
|
// This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
|
|
1101
|
-
const updatePromise = broadcastUpdate(key,
|
|
1099
|
+
const updatePromise = broadcastUpdate(key, valueAfterRemoving, 'set', hasChanged, wasRemoved);
|
|
1102
1100
|
|
|
1103
|
-
// If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
|
|
1104
|
-
if (!hasChanged) {
|
|
1101
|
+
// If the value has not changed or the key got removed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
|
|
1102
|
+
if (!hasChanged || wasRemoved) {
|
|
1105
1103
|
return updatePromise;
|
|
1106
1104
|
}
|
|
1107
1105
|
|
|
1108
|
-
return Storage.setItem(key,
|
|
1109
|
-
.catch((error) => evictStorageAndRetry(error, set, key,
|
|
1106
|
+
return Storage.setItem(key, valueAfterRemoving)
|
|
1107
|
+
.catch((error) => evictStorageAndRetry(error, set, key, valueAfterRemoving))
|
|
1110
1108
|
.then(() => updatePromise);
|
|
1111
1109
|
}
|
|
1112
1110
|
|
|
1113
1111
|
/**
|
|
1114
1112
|
* Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
|
|
1115
1113
|
* This method transforms an object like {'@MyApp_user': myUserValue, '@MyApp_key': myKeyValue}
|
|
1116
|
-
* to an array of key-value pairs in the above format
|
|
1114
|
+
* to an array of key-value pairs in the above format and removes key-value pairs that are being set to null
|
|
1117
1115
|
* @private
|
|
1118
1116
|
* @param {Record} data
|
|
1119
1117
|
* @return {Array} an array of key - value pairs <[key, value]>
|
|
1120
1118
|
*/
|
|
1121
1119
|
function prepareKeyValuePairsForStorage(data) {
|
|
1122
|
-
|
|
1120
|
+
const keyValuePairs = [];
|
|
1121
|
+
|
|
1122
|
+
_.forEach(data, (value, key) => {
|
|
1123
|
+
const {value: valueAfterRemoving, wasRemoved} = removeNullValues(key, value);
|
|
1124
|
+
|
|
1125
|
+
if (wasRemoved) return;
|
|
1126
|
+
|
|
1127
|
+
keyValuePairs.push([key, valueAfterRemoving]);
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
return keyValuePairs;
|
|
1123
1131
|
}
|
|
1124
1132
|
|
|
1125
1133
|
/**
|
|
@@ -1142,25 +1150,13 @@ function multiSet(data) {
|
|
|
1142
1150
|
|
|
1143
1151
|
const keyValuePairs = prepareKeyValuePairsForStorage(data);
|
|
1144
1152
|
|
|
1145
|
-
const updatePromises = _.map(
|
|
1153
|
+
const updatePromises = _.map(keyValuePairs, ([key, value]) => {
|
|
1146
1154
|
// Update cache and optimistically inform subscribers on the next tick
|
|
1147
|
-
cache.set(key,
|
|
1148
|
-
return scheduleSubscriberUpdate(key,
|
|
1155
|
+
cache.set(key, value);
|
|
1156
|
+
return scheduleSubscriberUpdate(key, value);
|
|
1149
1157
|
});
|
|
1150
1158
|
|
|
1151
|
-
|
|
1152
|
-
_.map(keyValuePairs, ([key, value]) => {
|
|
1153
|
-
const valueWithoutNull = removeNullValues(key, value);
|
|
1154
|
-
|
|
1155
|
-
if (valueWithoutNull === null) {
|
|
1156
|
-
return;
|
|
1157
|
-
}
|
|
1158
|
-
return [key, valueWithoutNull];
|
|
1159
|
-
}),
|
|
1160
|
-
Boolean,
|
|
1161
|
-
);
|
|
1162
|
-
|
|
1163
|
-
return Storage.multiSet(keyValuePairsWithoutNull)
|
|
1159
|
+
return Storage.multiSet(keyValuePairs)
|
|
1164
1160
|
.catch((error) => evictStorageAndRetry(error, multiSet, data))
|
|
1165
1161
|
.then(() => Promise.all(updatePromises));
|
|
1166
1162
|
}
|
|
@@ -1236,6 +1232,9 @@ function merge(key, changes) {
|
|
|
1236
1232
|
mergeQueue[key] = [changes];
|
|
1237
1233
|
|
|
1238
1234
|
mergeQueuePromise[key] = get(key).then((existingValue) => {
|
|
1235
|
+
// Calls to Onyx.set after a merge will terminate the current merge process and clear the merge queue
|
|
1236
|
+
if (mergeQueue[key] == null) return;
|
|
1237
|
+
|
|
1239
1238
|
try {
|
|
1240
1239
|
// We first only merge the changes, so we can provide these to the native implementation (SQLite uses only delta changes in "JSON_PATCH" to merge)
|
|
1241
1240
|
// We don't want to remove null values from the "batchedChanges", because SQLite uses them to remove keys from storage natively.
|
|
@@ -1250,10 +1249,7 @@ function merge(key, changes) {
|
|
|
1250
1249
|
delete mergeQueuePromise[key];
|
|
1251
1250
|
|
|
1252
1251
|
// If the batched changes equal null, we want to remove the key from storage, to reduce storage size
|
|
1253
|
-
|
|
1254
|
-
remove(key);
|
|
1255
|
-
return;
|
|
1256
|
-
}
|
|
1252
|
+
const {wasRemoved} = removeNullValues(key, batchedChanges);
|
|
1257
1253
|
|
|
1258
1254
|
// After that we merge the batched changes with the existing value
|
|
1259
1255
|
// We can remove null values from the "modifiedData", because "null" implicates that the user wants to remove a value from storage.
|
|
@@ -1271,10 +1267,10 @@ function merge(key, changes) {
|
|
|
1271
1267
|
const hasChanged = cache.hasValueChanged(key, modifiedData);
|
|
1272
1268
|
|
|
1273
1269
|
// This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
|
|
1274
|
-
const updatePromise = broadcastUpdate(key, modifiedData, hasChanged,
|
|
1270
|
+
const updatePromise = broadcastUpdate(key, modifiedData, 'merge', hasChanged, wasRemoved);
|
|
1275
1271
|
|
|
1276
1272
|
// If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
|
|
1277
|
-
if (!hasChanged || isClearing) {
|
|
1273
|
+
if (!hasChanged || isClearing || wasRemoved) {
|
|
1278
1274
|
return updatePromise;
|
|
1279
1275
|
}
|
|
1280
1276
|
|
|
@@ -19,7 +19,7 @@ function sendMessage(message) {
|
|
|
19
19
|
function subscribe(callback) {
|
|
20
20
|
subscriptions.push(callback);
|
|
21
21
|
channel.onmessage = (message) => {
|
|
22
|
-
subscriptions.forEach(c => c(message));
|
|
22
|
+
subscriptions.forEach((c) => c(message));
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -30,6 +30,4 @@ function disconnect() {
|
|
|
30
30
|
channel.close();
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export {
|
|
34
|
-
sendMessage, subscribe, disconnect,
|
|
35
|
-
};
|
|
33
|
+
export {sendMessage, subscribe, disconnect};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-onyx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.124",
|
|
4
4
|
"author": "Expensify, Inc.",
|
|
5
5
|
"homepage": "https://expensify.com",
|
|
6
6
|
"description": "State management for React Native",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
],
|
|
31
31
|
"react-native": "native.js",
|
|
32
32
|
"main": "native.js",
|
|
33
|
-
"browser": "
|
|
33
|
+
"browser": "lib/index.js",
|
|
34
34
|
"types": "lib/index.d.ts",
|
|
35
35
|
"scripts": {
|
|
36
36
|
"lint": "eslint .",
|