react-native-onyx 1.0.87 → 1.0.88

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/lib/Onyx.js CHANGED
@@ -49,6 +49,47 @@ let defaultKeyStates = {};
49
49
  // Connections can be made before `Onyx.init`. They would wait for this task before resolving
50
50
  const deferredInitTask = createDeferredTask();
51
51
 
52
+ let batchUpdatesPromise = null;
53
+ let batchUpdatesQueue = [];
54
+
55
+ /**
56
+ * We are batching together onyx updates. This helps with use cases where we schedule onyx updates after each other.
57
+ * This happens for example in the Onyx.update function, where we process API responses that might contain a lot of
58
+ * update operations. Instead of calling the subscribers for each update operation, we batch them together which will
59
+ * cause react to schedule the updates at once instead of after each other. This is mainly a performance optimization.
60
+ * @returns {Promise}
61
+ */
62
+ function maybeFlushBatchUpdates() {
63
+ if (batchUpdatesPromise) {
64
+ return batchUpdatesPromise;
65
+ }
66
+
67
+ batchUpdatesPromise = new Promise((resolve) => {
68
+ /* We use (setTimeout, 0) here which should be called once native module calls are flushed (usually at the end of the frame)
69
+ * We may investigate if (setTimeout, 1) (which in React Native is equal to requestAnimationFrame) works even better
70
+ * then the batch will be flushed on next frame.
71
+ */
72
+ setTimeout(() => {
73
+ const updatesCopy = batchUpdatesQueue;
74
+ batchUpdatesQueue = [];
75
+ batchUpdatesPromise = null;
76
+ unstable_batchedUpdates(() => {
77
+ updatesCopy.forEach((applyUpdates) => {
78
+ applyUpdates();
79
+ });
80
+ });
81
+
82
+ resolve();
83
+ }, 0);
84
+ });
85
+ return batchUpdatesPromise;
86
+ }
87
+
88
+ function batchUpdates(updates) {
89
+ batchUpdatesQueue.push(updates);
90
+ return maybeFlushBatchUpdates();
91
+ }
92
+
52
93
  /**
53
94
  * Uses a selector function to return a simplified version of sourceData
54
95
  * @param {Mixed} sourceData
@@ -638,9 +679,10 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
638
679
  * @param {Function} [mapping.callback]
639
680
  * @param {String} [mapping.selector]
640
681
  * @param {*|null} val
641
- * @param {String} matchedKey
682
+ * @param {String|undefined} matchedKey
683
+ * @param {Boolean} isBatched
642
684
  */
643
- function sendDataToConnection(mapping, val, matchedKey) {
685
+ function sendDataToConnection(mapping, val, matchedKey, isBatched) {
644
686
  // If the mapping no longer exists then we should not send any data.
645
687
  // This means our subscriber disconnected or withOnyx wrapped component unmounted.
646
688
  if (!callbackToStateMapping[mapping.connectionID]) {
@@ -661,7 +703,13 @@ function sendDataToConnection(mapping, val, matchedKey) {
661
703
  }
662
704
 
663
705
  PerformanceUtils.logSetStateCall(mapping, null, newData, 'sendDataToConnection');
664
- mapping.withOnyxInstance.setWithOnyxState(mapping.statePropertyName, newData);
706
+ if (isBatched) {
707
+ batchUpdates(() => {
708
+ mapping.withOnyxInstance.setWithOnyxState(mapping.statePropertyName, newData);
709
+ });
710
+ } else {
711
+ mapping.withOnyxInstance.setWithOnyxState(mapping.statePropertyName, newData);
712
+ }
665
713
  return;
666
714
  }
667
715
 
@@ -711,7 +759,7 @@ function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
711
759
  finalObject[matchingKeys[i]] = value;
712
760
  return finalObject;
713
761
  }, {}))
714
- .then(val => sendDataToConnection(mapping, val));
762
+ .then(val => sendDataToConnection(mapping, val, undefined, true));
715
763
  }
716
764
 
717
765
  /**
@@ -766,7 +814,9 @@ function connect(mapping) {
766
814
  // since there are none matched. In withOnyx() we wait for all connected keys to return a value before rendering the child
767
815
  // component. This null value will be filtered out so that the connected component can utilize defaultProps.
768
816
  if (matchingKeys.length === 0) {
769
- sendDataToConnection(mapping, null);
817
+ // Here we cannot use batching because the null value is expected to be set immediately for default props
818
+ // or they will be undefined.
819
+ sendDataToConnection(mapping, null, undefined, false);
770
820
  return;
771
821
  }
772
822
 
@@ -782,13 +832,13 @@ function connect(mapping) {
782
832
 
783
833
  // We did not opt into using waitForCollectionCallback mode so the callback is called for every matching key.
784
834
  for (let i = 0; i < matchingKeys.length; i++) {
785
- get(matchingKeys[i]).then(val => sendDataToConnection(mapping, val, matchingKeys[i]));
835
+ get(matchingKeys[i]).then(val => sendDataToConnection(mapping, val, matchingKeys[i], true));
786
836
  }
787
837
  return;
788
838
  }
789
839
 
790
840
  // If we are not subscribed to a collection key then there's only a single key to send an update for.
791
- get(mapping.key).then(val => sendDataToConnection(mapping, val, mapping.key));
841
+ get(mapping.key).then(val => sendDataToConnection(mapping, val, mapping.key, true));
792
842
  return;
793
843
  }
794
844
 
@@ -801,7 +851,7 @@ function connect(mapping) {
801
851
  }
802
852
 
803
853
  // If the subscriber is not using a collection key then we just send a single value back to the subscriber
804
- get(mapping.key).then(val => sendDataToConnection(mapping, val, mapping.key));
854
+ get(mapping.key).then(val => sendDataToConnection(mapping, val, mapping.key, true));
805
855
  return;
806
856
  }
807
857
 
@@ -835,47 +885,6 @@ function disconnect(connectionID, keyToRemoveFromEvictionBlocklist) {
835
885
  delete callbackToStateMapping[connectionID];
836
886
  }
837
887
 
838
- let batchUpdatesPromise = null;
839
- let batchUpdatesQueue = [];
840
-
841
- /**
842
- * We are batching together onyx updates. This helps with use cases where we schedule onyx updates after each other.
843
- * This happens for example in the Onyx.update function, where we process API responses that might contain a lot of
844
- * update operations. Instead of calling the subscribers for each update operation, we batch them together which will
845
- * cause react to schedule the updates at once instead of after each other. This is mainly a performance optimization.
846
- * @returns {Promise}
847
- */
848
- function maybeFlushBatchUpdates() {
849
- if (batchUpdatesPromise) {
850
- return batchUpdatesPromise;
851
- }
852
-
853
- batchUpdatesPromise = new Promise((resolve) => {
854
- /* We use (setTimeout, 0) here which should be called once native module calls are flushed (usually at the end of the frame)
855
- * We may investigate if (setTimeout, 1) (which in React Native is equal to requestAnimationFrame) works even better
856
- * then the batch will be flushed on next frame.
857
- */
858
- setTimeout(() => {
859
- const updatesCopy = batchUpdatesQueue;
860
- batchUpdatesQueue = [];
861
- batchUpdatesPromise = null;
862
- unstable_batchedUpdates(() => {
863
- updatesCopy.forEach((applyUpdates) => {
864
- applyUpdates();
865
- });
866
- });
867
-
868
- resolve();
869
- }, 0);
870
- });
871
- return batchUpdatesPromise;
872
- }
873
-
874
- function batchUpdates(updates) {
875
- batchUpdatesQueue.push(updates);
876
- return maybeFlushBatchUpdates();
877
- }
878
-
879
888
  /**
880
889
  * Schedules an update that will be appended to the macro task queue (so it doesn't update the subscribers immediately).
881
890
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "1.0.87",
3
+ "version": "1.0.88",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",