Skip to content

Instantly share code, notes, and snippets.

@palexs
Created November 29, 2019 14:01
Show Gist options
  • Save palexs/d615a3528cd675b508366e780606a03b to your computer and use it in GitHub Desktop.
Save palexs/d615a3528cd675b508366e780606a03b to your computer and use it in GitHub Desktop.
RN re-architecture

The New React Native Architecture

First announced in 2018, the React Native re-architecture is a massive effort Facebook has been undertaking to address some of the long-standing issues1.

"What do you dislike about React Native?" / "What do you dislike about React Native?" (June 2019 Edition)


Current Architecture

Facebook team wants to rethink the bridge (async message approach) to overcome its limitations.

inline


Discussions


JSI & JSC

In order for JavaScript code to run in a native mobile application it should rely on an engine to be interpreted.

The JSI is not part of React Native per se — it is a unified, lightweight, general-purpose layer for (theoretically) any JavaScript engine.

NATIVE <--> JSI <--> JS ENGINE


JSI & JSC

Benefits:

  • JSC could be swapped out more easily for other engines (e.g. ChakraCore by Microsoft and V8 by Google).
  • The cornerstone of the whole re-architecture — is that by using JSI, JavaScript can hold reference to C++ Host Objects and invoke methods on them. => JavaScript and Native realms will be truly aware of each other, and there won’t be any need to serialize to JSON the messages to pass across, removing all congestion on the bridge.

inline


Fabric and TurboModules

inline

inline


Fabric and TurboModules

Fabric - the re-architecture of the UI manager that aims to modernize the rendering layer of React Native.

Fabric will allow for the UI manager to create the Shadow Tree directly in C++.

=> increases the swiftness of the process by reducing the number of “jumps” across realms.

=> improves the responsiveness of the UI.


Fabric and TurboModules

By using the JSI, Fabric exposes the UI operations to JavaScript as functions => the new Shadow Tree (which determines what to really show on screen) is shared between the two realms, allowing straight interaction from both ends:

JAVASCRIPT <--> Shadow Tree in C++ <--> NATIVE

=> this direct control from the JavaScript side allows to have the priority queues from the new React 16 for the UI operations2, in order to have opt-in synchronous executions where it benefits performance.

=> improvements in common pitfalls like lists, navigation, and gesture handling.


Fabric and TurboModules

In the current implementation, the Native Modules used by JavaScript code (e.g. Bluetooth) need to be initialized when the app is opened — even when they’re not used — because of the “unawareness” between the realms.

The new TurboModules approach allows the JavaScript code to load each module only when it’s really needed, and to hold direct reference to it.

=> no more need to communicate using batched JSON messages on the bridge.

=> improves startup time for applications with lots of Native Modules.


Fabric and TurboModules

Interact with native code (ObjC/Java/Swift/Kotlin/Go/...) from React Native through JSI. (How to expose native code to JavaScript without going through the bridge?)

  1. Create a C++ binding.
  2. Install the binding into the JS runtime.
  3. Call native method from JS.

Fabric and TurboModules

1. Create a C++ binding.

The binding is a C++ class that implements the jsi::HostObject interface.

#include "Test.h"
#include <jsi/jsi.h>

namespace facebook {
  namespace react {

    // Exposes Test class to JavaScript realm.
    class TestBinding : public jsi::HostObject {
    public:
      // Installs TestBinding into JavaScript runtime.
      static void install(jsi::Runtime &runtime, std::shared_ptr<TestBinding> testBinding);

      TestBinding(std::unique_ptr<Test> test);

      // `jsi::HostObject` specific overloads.
      jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override;

    private:
      std::unique_ptr<Test> test_;
    };

  } // namespace react
} // namespace facebook

Fabric and TurboModules

1. Create a C++ binding.

jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override;
void TestBinding::install(jsi::Runtime &runtime,
                          std::shared_ptr<TestBinding> testBinding) {
  // What is the name that JS will use when it reaches for this, e.g. `global.nativeTest`
  auto testModuleName = "nativeTest";
  // Create a JS object version of our binding
  auto object = jsi::Object::createFromHostObject(runtime, testBinding);
  // Set the "nativeTest" property
  runtime.global().setProperty(runtime, testModuleName, std::move(object));
}
// Call from JS
global.nativeTest.foo

Fabric and TurboModules

2. Install the binding into the JS runtime.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
	...
	[[NSNotificationCenter defaultCenter] addObserver:self 
 	selector:@selector(handleJavaScriptDidLoadNotification:)
 	name:RCTJavaScriptDidLoadNotification object:bridge];
}
- (void)handleJavaScriptDidLoadNotification:(__unused NSNotification*)notification {
 	// Get the RCTCxxBridge from the bridge
 	RCTCxxBridge* bridge = notification.userInfo[@”bridge”];
 	// Get the runtime
 	facebook::jsi::Runtime* runtime = (facebook::jsi::Runtime*)bridge.runtime;
 	// Create the Test object
 	auto test = std::make_unique<facebook::react::Test>();
 	// Create the Test binding
 	std::shared_ptr<facebook::react::TestBinding> testBinding_ = 
  		std::make_shared<facebook::react::TestBinding>(std::move(test));
 	// Install it!
 	facebook::react::TestBinding::install((*runtime), testBinding_);
}

Fabric and TurboModules

3. Call native method from JS.

// Call from JS
global.nativeTest.foo
jsi::Value TestBinding::get(jsi::Runtime &runtime, const jsi::PropNameID &name) {
  auto methodName = name.utf8(runtime);
  auto &test = *test_;

  if (methodName == "foo") {
    return jsi::Function::createFromHostFunction(
        runtime, name, 0, [&test]
        (jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *arguments, size_t count)
        	-> jsi::Value {
        		return test.foo();
        	}
        );
  }
  return jsi::Value::undefined();
}

Fabric and TurboModules

inline


CodeGen

The React Native team is also doubling down on the presence of a static type checker (either Flow or TypeScript) in the code.

In particular, they are working on a tool called CodeGen to "automate" the compatibility between JS and the native side. By using the typed JavaScript as the source of truth, CodeGen can generate the interface files needed by Fabric and TurboModules to send messages across the realms with confidence.

inline


CodeGen

In both TurboModule and Fabric, interface available to JavaScript could be defined using Flow or TypeScript. This interface definition can be further leveraged to generate many of the C++ classes, and the interfaces/protocols for Java/ObjC implementations.

For example, in case of TurboModules, the C++ class that wraps the ObjC/Java class and exposes the methods using a JSI object can be generated. This will ensure that all JavaScript calls have implementations available on the native side, and will continue to ensure this with over the air updates like CodePush.

Typed JS -> [CodeGen] -> JSI Bindings/ObjC Protocols/Abstract Java Classes + Schemas -> Fabric/TurboModules


CodeGen

CodeGen automates creation of JSI C++ bindings and abstracts away C++ code from RN developers.

Benefits:

  • Compile-time type safety!
  • Safe over-the-air updates

Lean Core

Lean Core initiative has an aim to break down bloated React Native codebase into different repositories.

Benefits:

  • Make the codebase more approachable
  • Deprecate older modules
  • Reduce the weight of a generated app => faster startup time
  • Help the community move fast and enable PRs to be reviewed and merged quicker

Old vs New Architecture

inline fillinline fill


Resources

Articles: The New React Native Architecture Explained. Part One: React and Codegen The New React Native Architecture Explained. Part Two: JSI and JSC The New React Native Architecture Explained. Part Three: Fabric and TurboModules The New React Native Architecture Explained. Part Four: Lean Core React Native's New Architecture - Glossary of Terms React Native JSI Challenge React Native JSI Challenge #2 Interacting with Go from React Native through JSI

Videos: React Native's New Architecture - Parashuram N - React Conf 2018 React Native EU 2019: Alexey Kureev - React Native CodeGen

Footnotes

  1. Concurrent React and the Scheduler / Suspense suspends a component rendering and renders a fallback component until a condition is met.

@palexs
Copy link
Author

palexs commented Nov 29, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment