Fabric architecture for react-native has created excitement among all react-native mobile application developers since its inception in 2018. When we come to develop mobile applications, all these cross-platform languages are being compares with their native app counterparts. Mainly because there is always a visible performance gap. Mostly due to the performance gap. With this in mind, the core principles of fabric are to unify more render logic in C++. And improve interoperability with host platforms, and unlock new capabilities for React Native.

What motivates the new renderer

The rendering architecture was creates to unlock better user experiences that weren’t possible with legacy architecture. Some examples include:

  • With improved interoperability between host views and React views, the renderer is able to measure and render React surfaces synchronously. In the legacy architecture, React Native layout was asynchronous which led to a layout “jump” issue when embedding a React Native rendered view in a host view.
  • With the support of multi-priority and synchronous events, the renderer can prioritize certain user interactions to ensure they are handled in a timely manner.
  • Integration with React Suspense which allows for a more intuitive design of data fetching in React apps.
  • Enable React Concurrent Features on React Native.
  • Easier to implement server-side rendering for React Native.

Benefits of fabric architecture

The new architecture also provides benefits in code quality, performance, and extensibility:

  • Type safety: code generation to ensure type safety across the JS and host platforms. The code generation uses JavaScript component declarations as a source of truth to generate C++ structs to hold the props. The mismatch between JavaScript and host component props triggers a build error.
  • Shared C++ core: the renderer is implemented in C++ and the core is shared among platforms. This increases consistency and makes it easier to adopt React Native on new platforms.
  • Better Host Platform Interoperability: Synchronous and thread-safe layout calculations improve user experiences when embedding host components into React Native. This means easier integration with host platform frameworks that require synchronous APIs.
  • Improved Performance: With the new cross-platform implementation of the renderer system, every platform benefits from performance improvements that may have been motivated by the limitations of one platform. For example, view flattening was originally a performance solution for Android and is now provided by default on Android and iOS.
  • Consistency: The new render system is cross-platform, so it is easier to keep consistency among different platforms.
  • Faster Startup: Host components are lazily initialized by default.
  • Less serialization of data between JS and host platform: React is used to transfer data between JavaScript and host platform as serialized JSON. The new renderer improves the transfer of data by accessing JavaScript values directly using JavaScript Interfaces (JSI).

Improved communication between JavaScript and the native threads

If react native application is run in the architecture. The javascript code is bundles together into a package calles js bundle. And the native code is kept separate. The javascript thread runs the js bundle, and the native/ui thread runs the native modules and handles ui rendering. The communication between the js and native threads is enables by a bridge. Which sends data to the native threads after serializing it as json. This bridge can only handle asynchronous communication.

with fabric, logic is renderes in c++, which improves the interoperability between the host platforms. The fabric renderer is implementes in c++, and the c++ core is shares among platforms. Which providing greater consistency and making react native easier to adopt on new platforms.

Furthermore, the new architecture decouples the JavaScript interface from the engine. It enabling the use of other JavaScript engines such as Hermes, V8, or Chakra.


Fabric

The improves interoperability between react native and host views is enables by the c++ core. Which is shares among different host platforms and enables react native to render react surfaces synchronously.

It wasn’t always this way — in the legacy React Native architecture, the layout was asynchronous. Which led to a layout “jump” issue when embedding a React Native rendered view in a host view.

The three phases of the Fabric render pipeline

The render pipeline occurs in three phases:

  1. The Render phase
  2. The Commit phase
  3. The Mount phase

Let’s look into each of them more closely.

The Render phase in fabric architecture

In this phase, React executes product logic to create React element trees, which consist of React elements. A React Element is a plain JavaScript object that describes what should appear on the application screen.

The react element tree is uses to render the react shadow tree in c++. The react shadow tree is creates by the fabric renderer and consists of react shadow nodes. Which are objects that represent the react host components that are to be mountes and contain props that originate from javascript.

const App = () => {
  return (
    <View>
      <Text>App.js</Text>
    </View>
  );
};

In the Render phase, as each React element is invoke, the renderer synchronously creates a React shadow node. Take note that this synchronous creation of a React shadow node only occurs for React host components. And not for React composite components. When translated into a React Shadow Block, the above code would see the <View> translated into a ViewShadowNode object.

A great thing about the new renderer is that any parent-child relationship between React element nodes will correspond to the relationships among React shadow nodes. The above process shows how the React shadow tree is assemble. Once the React shadow tree is complete, the renderer triggers a commit of the React element tree.

Below is a visual representation of the render phase:

Render phase of fabric

The Commit phase in fabric architecture

The cross-platform layout engine Yoga is hugely important in handling operations. That happen during the commit phase, which consists of two operations: layout calculation and tree promotion.

The layout calculation calculates the position and size of each React shadow node. This is achieve by invoking Yoga to calculate the layout of each React shadow node.

Once the calculation determines the amount of available space, the Tree Promotion operation promotes the new React shadow tree as the next tree to be mounted. This promotion represents the latest state of the React element tree.

Commit phase of fabric

The Mount phase of fabric architecture

This is the phase in which the react shadow tree (which contains the data from the layout calculation) is transformes into a host view tree with renderes pixels on the screen. The Fabric renderer creates a corresponding host view for each React shadow node and mounts it on the screen.

Mount phase of fabric

TurboModules

The TurboModules system is an enhancement of Native Modules. In their current architecture, a table holds the module registry, and when the javascript code calls a specific native module, the indices of the module and the methods are passes to java/objc, which invoke the specific methods.

A proposed solution to the eager initialization of Native packages is the use of the LazyReactPackage, but this is not an effective method because the annotation processor ReactModuleSpecProcessor does not run with Gradle; hence, the LazyReactPackage doesn’t work with the open source release.

With the new implementation, JavaScript will expose a top-level Native Module Proxy, called global.__turboModuleProxy, to access a native module. If we used the example of "SampleTurboModule", the application code will call the require('NativeSampleTurboModule'). In the NativeSampleTurboModule, the TurboModuleRegistry.getEnforcing() function is called, which holds a reference to the Native Modules and then calls global.__turboModuleProxy("SampleTurboModule").

This triggers the JSI function and a getModule function is invoked, which is defined for Java and ObjC, and returns a JSI object for the specific TurboModule (i.e., GeoLocation, FileStorage, DeviceInformation) that previously would have been initialized at app startup. This allows javascript code to load each module when it is requires. Instead of initializing them before the app is openes.

This is all about the fabric architecture.

React Native Fabric (UI-Layer Re-architecture)👓 TRANSPARENCY

Architecture Overview · React Native

Fabric · React Native

Render, Commit, and Mount · React Native

ADVANCED

View Flattening · React Native

Threading Model · React Native

0.69.0 ONWARDS

Bundled Hermes · React Native

To know about react testing read here

Get to know about Rently at https://use.rently.com/

To learn more about Engineering topics visit – https://engineering.rently.com

This Post Has One Comment

Leave a Reply

Login with