react-native-onyx 2.0.2 → 2.0.4
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 +9 -9
- package/dist/Onyx.js +30 -24
- package/dist/OnyxCache.d.ts +2 -1
- package/dist/OnyxCache.js +5 -2
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -430,14 +430,14 @@ The action logs use this naming convention:
|
|
|
430
430
|
|
|
431
431
|
# Development
|
|
432
432
|
|
|
433
|
-
|
|
434
|
-
link a local version of Onyx during development
|
|
433
|
+
React Native bundles source code using the `metro` bundler. Until React Native 0.73, `metro` does not follow symlinks, so we can't use `npm link` to
|
|
434
|
+
link a local version of Onyx during development. Fortunately, we have set up a workflow that's easy to follow and enables
|
|
435
|
+
you to edit the Onyx source directly in the Onyx repo, and have those changes hot-reload in a React Native project in realtime.
|
|
435
436
|
|
|
436
|
-
|
|
437
|
+
1. In one terminal tab, open the `react-native-onyx` directory and run `npm run build:watch`
|
|
438
|
+
2. In another terminal tab, open your React Native project and run `npx link publish <path_to_onyx_directory_on_your_machine>`
|
|
439
|
+
3. Then run your React Native project as normal!
|
|
437
440
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
3. Optional: run `npm run build` (if you're working or want to test on a non react-native project)
|
|
442
|
-
- `npm link` would actually work outside of `react-native` and it can be used to link Onyx locally for a web only project
|
|
443
|
-
4. Copy Onyx to consumer project's `node_modules/react-native-onyx`
|
|
441
|
+
Now you can make changes directly to the `react-native-onyx` source code and your React Native project should-hot reload with those changes in realtime.
|
|
442
|
+
|
|
443
|
+
_Note:_ If you want to unlink `react-native-onyx`, simply run `npm install` from your React Native project directory again. That will reinstall `react-native-onyx` from npm.
|
package/dist/Onyx.js
CHANGED
|
@@ -510,11 +510,12 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
510
510
|
* @private
|
|
511
511
|
* @param {String} key
|
|
512
512
|
* @param {*} data
|
|
513
|
+
* @param {*} prevData
|
|
513
514
|
* @param {Function} [canUpdateSubscriber] only subscribers that pass this truth test will be updated
|
|
514
515
|
* @param {boolean} [notifyRegularSubscibers=true]
|
|
515
516
|
* @param {boolean} [notifyWithOnyxSubscibers=true]
|
|
516
517
|
*/
|
|
517
|
-
function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = true, notifyWithOnyxSubscibers = true) {
|
|
518
|
+
function keyChanged(key, data, prevData, canUpdateSubscriber = () => true, notifyRegularSubscibers = true, notifyWithOnyxSubscibers = true) {
|
|
518
519
|
// Add or remove this key from the recentlyAccessedKeys lists
|
|
519
520
|
if (!underscore_1.default.isNull(data)) {
|
|
520
521
|
addLastAccessedKey(key);
|
|
@@ -528,7 +529,7 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
528
529
|
const stateMappingKeys = underscore_1.default.keys(callbackToStateMapping);
|
|
529
530
|
for (let i = 0; i < stateMappingKeys.length; i++) {
|
|
530
531
|
const subscriber = callbackToStateMapping[stateMappingKeys[i]];
|
|
531
|
-
if (!subscriber || !isKeyMatch(subscriber.key, key) ||
|
|
532
|
+
if (!subscriber || !isKeyMatch(subscriber.key, key) || !canUpdateSubscriber(subscriber)) {
|
|
532
533
|
continue;
|
|
533
534
|
}
|
|
534
535
|
// Subscriber is a regular call to connect() and provided a callback
|
|
@@ -556,13 +557,13 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
556
557
|
// returned by the selector and only when the selected data has changed.
|
|
557
558
|
if (subscriber.selector) {
|
|
558
559
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
559
|
-
const
|
|
560
|
-
const
|
|
560
|
+
const prevWithOnyxData = prevState[subscriber.statePropertyName];
|
|
561
|
+
const newWithOnyxData = {
|
|
561
562
|
[key]: getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state),
|
|
562
563
|
};
|
|
563
|
-
const prevDataWithNewData = Object.assign(Object.assign({},
|
|
564
|
-
if (!(0, fast_equals_1.deepEqual)(
|
|
565
|
-
PerformanceUtils.logSetStateCall(subscriber,
|
|
564
|
+
const prevDataWithNewData = Object.assign(Object.assign({}, prevWithOnyxData), newWithOnyxData);
|
|
565
|
+
if (!(0, fast_equals_1.deepEqual)(prevWithOnyxData, prevDataWithNewData)) {
|
|
566
|
+
PerformanceUtils.logSetStateCall(subscriber, prevWithOnyxData, newWithOnyxData, 'keyChanged', key);
|
|
566
567
|
return {
|
|
567
568
|
[subscriber.statePropertyName]: prevDataWithNewData,
|
|
568
569
|
};
|
|
@@ -584,8 +585,8 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
584
585
|
// If the subscriber has a selector, then the component's state must only be updated with the data
|
|
585
586
|
// returned by the selector and only if the selected data has changed.
|
|
586
587
|
if (subscriber.selector) {
|
|
587
|
-
subscriber.withOnyxInstance.setStateProxy((
|
|
588
|
-
const previousValue = getSubsetOfData(
|
|
588
|
+
subscriber.withOnyxInstance.setStateProxy(() => {
|
|
589
|
+
const previousValue = getSubsetOfData(prevData, subscriber.selector, subscriber.withOnyxInstance.state);
|
|
589
590
|
const newValue = getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state);
|
|
590
591
|
if (!(0, fast_equals_1.deepEqual)(previousValue, newValue)) {
|
|
591
592
|
return {
|
|
@@ -598,15 +599,15 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
598
599
|
}
|
|
599
600
|
// If we did not match on a collection key then we just set the new data to the state property
|
|
600
601
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
601
|
-
const
|
|
602
|
+
const prevWithOnyxData = prevState[subscriber.statePropertyName];
|
|
602
603
|
// Avoids triggering unnecessary re-renders when feeding empty objects
|
|
603
|
-
if (utils_1.default.areObjectsEmpty(data,
|
|
604
|
+
if (utils_1.default.areObjectsEmpty(data, prevWithOnyxData)) {
|
|
604
605
|
return null;
|
|
605
606
|
}
|
|
606
|
-
if (
|
|
607
|
+
if (prevWithOnyxData === data) {
|
|
607
608
|
return null;
|
|
608
609
|
}
|
|
609
|
-
PerformanceUtils.logSetStateCall(subscriber,
|
|
610
|
+
PerformanceUtils.logSetStateCall(subscriber, prevData, data, 'keyChanged', key);
|
|
610
611
|
return {
|
|
611
612
|
[subscriber.statePropertyName]: data,
|
|
612
613
|
};
|
|
@@ -829,12 +830,13 @@ function disconnect(connectionID, keyToRemoveFromEvictionBlocklist) {
|
|
|
829
830
|
*
|
|
830
831
|
* @param {String} key
|
|
831
832
|
* @param {*} value
|
|
833
|
+
* @param {*} prevValue
|
|
832
834
|
* @param {Function} [canUpdateSubscriber] only subscribers that pass this truth test will be updated
|
|
833
835
|
* @returns {Promise}
|
|
834
836
|
*/
|
|
835
|
-
function scheduleSubscriberUpdate(key, value, canUpdateSubscriber) {
|
|
836
|
-
const promise = Promise.resolve().then(() => keyChanged(key, value, canUpdateSubscriber, true, false));
|
|
837
|
-
batchUpdates(() => keyChanged(key, value, canUpdateSubscriber, false, true));
|
|
837
|
+
function scheduleSubscriberUpdate(key, value, prevValue, canUpdateSubscriber = () => true) {
|
|
838
|
+
const promise = Promise.resolve().then(() => keyChanged(key, value, prevValue, canUpdateSubscriber, true, false));
|
|
839
|
+
batchUpdates(() => keyChanged(key, value, prevValue, canUpdateSubscriber, false, true));
|
|
838
840
|
return Promise.all([maybeFlushBatchUpdates(), promise]);
|
|
839
841
|
}
|
|
840
842
|
/**
|
|
@@ -859,8 +861,9 @@ function scheduleNotifyCollectionSubscribers(key, value) {
|
|
|
859
861
|
* @return {Promise}
|
|
860
862
|
*/
|
|
861
863
|
function remove(key) {
|
|
864
|
+
const prevValue = OnyxCache_1.default.getValue(key, false);
|
|
862
865
|
OnyxCache_1.default.drop(key);
|
|
863
|
-
scheduleSubscriberUpdate(key, null);
|
|
866
|
+
scheduleSubscriberUpdate(key, null, prevValue);
|
|
864
867
|
return storage_1.default.removeItem(key);
|
|
865
868
|
}
|
|
866
869
|
/**
|
|
@@ -920,6 +923,7 @@ function evictStorageAndRetry(error, onyxMethod, ...args) {
|
|
|
920
923
|
function broadcastUpdate(key, value, method, hasChanged, wasRemoved = false) {
|
|
921
924
|
// Logging properties only since values could be sensitive things we don't want to log
|
|
922
925
|
Logger.logInfo(`${method}() called for key: ${key}${underscore_1.default.isObject(value) ? ` properties: ${underscore_1.default.keys(value).join(',')}` : ''}`);
|
|
926
|
+
const prevValue = OnyxCache_1.default.getValue(key, false);
|
|
923
927
|
// Update subscribers if the cached value has changed, or when the subscriber specifically requires
|
|
924
928
|
// all updates regardless of value changes (indicated by initWithStoredValues set to false).
|
|
925
929
|
if (hasChanged && !wasRemoved) {
|
|
@@ -928,7 +932,7 @@ function broadcastUpdate(key, value, method, hasChanged, wasRemoved = false) {
|
|
|
928
932
|
else {
|
|
929
933
|
OnyxCache_1.default.addToAccessedKeys(key);
|
|
930
934
|
}
|
|
931
|
-
return scheduleSubscriberUpdate(key, value, (subscriber) => hasChanged || subscriber.initWithStoredValues === false);
|
|
935
|
+
return scheduleSubscriberUpdate(key, value, prevValue, (subscriber) => hasChanged || subscriber.initWithStoredValues === false);
|
|
932
936
|
}
|
|
933
937
|
/**
|
|
934
938
|
* @param {String} key
|
|
@@ -1011,9 +1015,10 @@ function prepareKeyValuePairsForStorage(data) {
|
|
|
1011
1015
|
function multiSet(data) {
|
|
1012
1016
|
const keyValuePairs = prepareKeyValuePairsForStorage(data);
|
|
1013
1017
|
const updatePromises = underscore_1.default.map(keyValuePairs, ([key, value]) => {
|
|
1018
|
+
const prevValue = OnyxCache_1.default.getValue(key, false);
|
|
1014
1019
|
// Update cache and optimistically inform subscribers on the next tick
|
|
1015
1020
|
OnyxCache_1.default.set(key, value);
|
|
1016
|
-
return scheduleSubscriberUpdate(key, value);
|
|
1021
|
+
return scheduleSubscriberUpdate(key, value, prevValue);
|
|
1017
1022
|
});
|
|
1018
1023
|
return storage_1.default.multiSet(keyValuePairs)
|
|
1019
1024
|
.catch((error) => evictStorageAndRetry(error, multiSet, data))
|
|
@@ -1130,10 +1135,10 @@ function merge(key, changes) {
|
|
|
1130
1135
|
*/
|
|
1131
1136
|
function initializeWithDefaultKeyStates() {
|
|
1132
1137
|
return storage_1.default.multiGet(underscore_1.default.keys(defaultKeyStates)).then((pairs) => {
|
|
1133
|
-
const
|
|
1134
|
-
const merged = utils_1.default.fastMerge(
|
|
1138
|
+
const existingDataAsObject = underscore_1.default.object(pairs);
|
|
1139
|
+
const merged = utils_1.default.fastMerge(existingDataAsObject, defaultKeyStates);
|
|
1135
1140
|
OnyxCache_1.default.merge(merged);
|
|
1136
|
-
underscore_1.default.each(merged, (val, key) => keyChanged(key, val));
|
|
1141
|
+
underscore_1.default.each(merged, (val, key) => keyChanged(key, val, existingDataAsObject));
|
|
1137
1142
|
});
|
|
1138
1143
|
}
|
|
1139
1144
|
/**
|
|
@@ -1201,7 +1206,7 @@ function clear(keysToPreserve = []) {
|
|
|
1201
1206
|
const updatePromises = [];
|
|
1202
1207
|
// Notify the subscribers for each key/value group so they can receive the new values
|
|
1203
1208
|
underscore_1.default.each(keyValuesToResetIndividually, (value, key) => {
|
|
1204
|
-
updatePromises.push(scheduleSubscriberUpdate(key, value));
|
|
1209
|
+
updatePromises.push(scheduleSubscriberUpdate(key, value, OnyxCache_1.default.getValue(key, false)));
|
|
1205
1210
|
});
|
|
1206
1211
|
underscore_1.default.each(keyValuesToResetAsCollection, (value, key) => {
|
|
1207
1212
|
updatePromises.push(scheduleNotifyCollectionSubscribers(key, value));
|
|
@@ -1401,8 +1406,9 @@ function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCach
|
|
|
1401
1406
|
Promise.all([addAllSafeEvictionKeysToRecentlyAccessedList(), initializeWithDefaultKeyStates()]).then(deferredInitTask.resolve);
|
|
1402
1407
|
if (shouldSyncMultipleInstances && underscore_1.default.isFunction(storage_1.default.keepInstancesSync)) {
|
|
1403
1408
|
storage_1.default.keepInstancesSync((key, value) => {
|
|
1409
|
+
const prevValue = OnyxCache_1.default.getValue(key, false);
|
|
1404
1410
|
OnyxCache_1.default.set(key, value);
|
|
1405
|
-
keyChanged(key, value);
|
|
1411
|
+
keyChanged(key, value, prevValue);
|
|
1406
1412
|
});
|
|
1407
1413
|
}
|
|
1408
1414
|
}
|
package/dist/OnyxCache.d.ts
CHANGED
|
@@ -39,9 +39,10 @@ declare class OnyxCache {
|
|
|
39
39
|
/**
|
|
40
40
|
* Get a cached value from storage
|
|
41
41
|
* @param {string} key
|
|
42
|
+
* @param {Boolean} [shouldReindexCache] – This is an LRU cache, and by default accessing a value will make it become last in line to be evicted. This flag can be used to skip that and just access the value directly without side-effects.
|
|
42
43
|
* @returns {*}
|
|
43
44
|
*/
|
|
44
|
-
getValue(key: string): any;
|
|
45
|
+
getValue(key: string, shouldReindexCache?: boolean | undefined): any;
|
|
45
46
|
/**
|
|
46
47
|
* Check whether cache has data for the given key
|
|
47
48
|
* @param {string} key
|
package/dist/OnyxCache.js
CHANGED
|
@@ -52,10 +52,13 @@ class OnyxCache {
|
|
|
52
52
|
/**
|
|
53
53
|
* Get a cached value from storage
|
|
54
54
|
* @param {string} key
|
|
55
|
+
* @param {Boolean} [shouldReindexCache] – This is an LRU cache, and by default accessing a value will make it become last in line to be evicted. This flag can be used to skip that and just access the value directly without side-effects.
|
|
55
56
|
* @returns {*}
|
|
56
57
|
*/
|
|
57
|
-
getValue(key) {
|
|
58
|
-
|
|
58
|
+
getValue(key, shouldReindexCache = true) {
|
|
59
|
+
if (shouldReindexCache) {
|
|
60
|
+
this.addToAccessedKeys(key);
|
|
61
|
+
}
|
|
59
62
|
return this.storageMap[key];
|
|
60
63
|
}
|
|
61
64
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-onyx",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"author": "Expensify, Inc.",
|
|
5
5
|
"homepage": "https://expensify.com",
|
|
6
6
|
"description": "State management for React Native",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
33
|
"test": "jest",
|
|
34
34
|
"build": "tsc -p tsconfig.build.json && cp ./lib/*.d.ts ./dist",
|
|
35
|
+
"build:watch": "nodemon --watch lib --exec \"npm run build && npm pack\"",
|
|
35
36
|
"build:docs": "node buildDocs.js",
|
|
36
37
|
"lint-tests": "eslint tests/**",
|
|
37
38
|
"prettier": "prettier --write ."
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"jest-cli": "^26.5.2",
|
|
66
67
|
"jsdoc-to-markdown": "^7.1.0",
|
|
67
68
|
"metro-react-native-babel-preset": "^0.77.0",
|
|
69
|
+
"nodemon": "^3.0.3",
|
|
68
70
|
"prettier": "^2.8.8",
|
|
69
71
|
"prop-types": "^15.7.2",
|
|
70
72
|
"react": "18.2.0",
|
|
@@ -81,9 +83,9 @@
|
|
|
81
83
|
"idb-keyval": "^6.2.1",
|
|
82
84
|
"react": ">=18.1.0",
|
|
83
85
|
"react-dom": ">=18.1.0",
|
|
86
|
+
"react-native-device-info": "^10.3.0",
|
|
84
87
|
"react-native-performance": "^5.1.0",
|
|
85
|
-
"react-native-quick-sqlite": "^8.0.0-beta.2"
|
|
86
|
-
"react-native-device-info": "^10.3.0"
|
|
88
|
+
"react-native-quick-sqlite": "^8.0.0-beta.2"
|
|
87
89
|
},
|
|
88
90
|
"peerDependenciesMeta": {
|
|
89
91
|
"idb-keyval": {
|