Andrei Calazans

React Native Weekly - W22 2021

☕️ 6 min read

Welcome to the eleventh edition of React Native Weekly. This is the retrospect of week 22 of 2021.

Reach out to me via Twitter if you have any feedback, and don’t forget to subscribe!

Release of Reanimated 2.2.0

This release is more bug-fix oriented but expect something special in the next alpha release Eyes

  • Fixed debugging issue on Hermes
  • Support for @quilted/react-testing
  • withDecay improvements

Check full release notes for details: https://github.com/software-mansion/react-native-reanimated/releases/tag/2.2.0

ClojureScript With React Native

Thomas tweeted a blogpost that detailed Vouchio’s journey with this stack.

My take is ClojureScript has an amazing Repl environment that allows you to quickly iterate. I know we could already do something similar with Node.js REPL, I wonder if we could with React Native.

Expo CLI Run Commands

Expo is bringing much of the abstraction that existed in the “managed” workflow to the “bare” workflow. For those who don’t know, Expo initially only had a “managed” workflow where everything was handled internally by their SDK and if you needed to make native changes you would have to eject. They changed this and broke it up into two.

CLI Run Commands abstracts away a few annoying things like:

  • Code Signing
  • Better Warnings & Errors
  • Prebuilding and config plugins

The article is full of details and I recommend the read. Given how Expo is invested in open source culture and React Native it is only time before they become the absolute way of making React Native apps.

Upgrade jsc-android to 250230.2.1 (#31304)

Upgrade jsc-android to latest stable version. This should finally fix https://github.com/facebook/react-native/issues/25494. Before Hermes totally replaced JSC, it should be worth to have this and make JSC stable.

Commit

Refactor LockFreeEventDispatcherImpl

Joshua Gross joshuagross@fb.com refactored the Java implementation of LockFreeEventDispatcherImpl, responsible for dispatching events between the JS and native threads.

He found that LockFreeEventDispatcherImpl was triple queuing events when in “staging”, “dispatch events” in JS, and again when sent to C++. The refactor eliminated one level of queuing - the “dispatch events” queue for the JS thread.

There were some more implementation details that impacted the AsyncEventBeat in C++, check out the Commit.

Refactor EventQueue

Another refactor took place where Samuel Susla samuelsusla@fb.com simplified the core EventQueue implementation by pulling out some logic and placing it in EventQueueProcessor. It now holds the logic for flushing events.


+class EventQueueProcessor {
+ public:
+  EventQueueProcessor(EventPipe eventPipe, StatePipe statePipe);
+
+  void flushEvents(jsi::Runtime &runtime, std::vector<RawEvent> &&events) const;
+  void flushStateUpdates(std::vector<StateUpdate> &&states) const;
+
+ private:
+  EventPipe const eventPipe_;
+  StatePipe const statePipe_;
+};

Add Getters For Event Priority To UIManager

Commit

Introduce a way to specify React priority for events.

These APIs will be called from React here: https://github.com/facebook/react/blob/0e100ed00fb52cfd107db1d1081ef18fe4b9167f/packages/react-native-renderer/src/ReactFabricHostConfig.js#L345

The implementation adds an enum for event priorities and binds them to the UIManager.

Enum:

+enum class ReactEventPriority {
+  /*
+   * Event priority is unspecified.
+   */
+  Default,
+
+  /*
+   * User events that happen at discrete times, like
+   * input into text field.
+   */
+  Discrete,
+
+  /*
+   * “fluid” user events that happen many times over a short period of time like
+   * scrolling.
+   */
+  Continuous,
+

UIManager binding:

diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp
index 31b91339b2..c873c7f968 100644
--- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp
+++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp

...

 void UIManagerBinding::invalidate() const {
@@ -774,6 +777,18 @@ jsi::Value UIManagerBinding::get(
         });
   }
 
+  if (methodName == "unstable_currentEventPriority") {
+    return jsi::Value(serialize(currentEventPriority_));
+  }
+
+  if (methodName == "unstable_DefaultEventPriority") {
+    return jsi::Value(serialize(ReactEventPriority::Default));
+  }
+
+  if (methodName == "unstable_DiscreteEventPriority") {
+    return jsi::Value(serialize(ReactEventPriority::Discrete));
+  }
+
   return jsi::Value::undefined();
 }

Return Event Priorities To JS

Commit

This is a mechanism that will guess event’s React priority based on other events ongoing on the platform.

If an event happens within span of ContinuousStart -> ContinuousEnd and its category is unspecified, we deduce it’s React priority to be default. All other events are discrete.

Special case: onScroll, which is always treated as “Default”.


diff --git a/ReactCommon/react/renderer/core/RawEvent.h b/ReactCommon/react/renderer/core/RawEvent.h
index 03286a2ac0..fc3de781d2 100644
--- a/ReactCommon/react/renderer/core/RawEvent.h
+++ b/ReactCommon/react/renderer/core/RawEvent.h
+struct RawEvent {
+  /*
+   * Defines category of a native platform event. This is used to deduce types
+   * of events for Concurrent Mode.
+   */
+  enum class Category {
+    /*
+     * Start of a continuous event. To be used with touchStart.
+     */
+    ContinuousStart,
+
+    /*
+     * End of a continuous event. To be used with touchEnd.
+     */
+    ContinuousEnd,
+
+    /*
+     * Priority for this event will be determined from other events in the
+     * queue. If it is triggered by continuous event, its priority will be
+     * default. If it is not triggered by continuous event, its priority will be
+     * discrete.
+     */
+    Unspecified,
+
+    /*
+     * Forces discrete type for the event. Regardless if continuous event is
+     * ongoing.
+     */
+    Discrete,
+
+    /*
+     * Forces continuous type for the event. Regardless if continuous event
+     * isn't ongoing.
+     */
+    Continuous
+  };
+

Introduces synchronous access to the runtime from RuntimeScheduler

This commit gives RuntimeScheduler synchronous access to the runtime, see comment in diff to better understand.

diff --git a/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h b/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h
index aa2ae5b8f0..013079a62b 100644
--- a/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h
+++ b/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h
@@ -25,6 +25,16 @@ class RuntimeScheduler final {
 
   void scheduleWork(std::function<void(jsi::Runtime &)> callback) const;
 
+  /*
+   * Grants access to the runtime synchronously on the caller's thread.
+   *
+   * Shouldn't be called directly. it is expected to be used
+   * by dispatching a synchronous event via event emitter in your native
+   * component.
+   */
+  void executeNowOnTheSameThread(
+      std::function<void(jsi::Runtime &runtime)> callback) const;
+

Fix for ScrollView race condition between C++ state update and onScroll

There is a possibility of race between updating scrollview’s state and virtualised list asking for layout of individual cells. To make sure the race doesn’t happen, state must be updated before dispatching onScroll event.

Commit

Adds accessiblity actions on core components (#31532) [External Contribution]

Contribution by Dennis Urtubia dennisurtubia@alunos.utfpr.edu.br.

Summary: Android: Adding custom actions (https://github.com/facebook/react-native/issues/30854). Adds accessiblity actions on core components (Button, TextInput, Text, and Picker).

He added support for “accessibilityActions” and"onAccessbilityAction" props.

+          <Button
+            accessible={true}
+            accessibilityActions={[
+              {name: 'activate', label: 'activate label'},
+              {name: 'copy', label: 'copy label'},
+            ]}
+            onAccessibilityAction={event => {
+              switch (event.nativeEvent.actionName) {
+                case 'activate':
+                  Alert.alert('Alert', 'Activate accessiblity action');
+                  break;
+                case 'copy':
+                  Alert.alert('Alert', 'copy action success');
+                  break;
+              }
+            }}

That Is It!

That’s it for this week. If you want to see more checkout previous week’s posts here. Subscribe to get notified when new posts are out = )