Skip to content

Latest commit

 

History

History
115 lines (76 loc) · 6.64 KB

enable-libraries.md

File metadata and controls

115 lines (76 loc) · 6.64 KB

../README.md#Guides

Enable the New Architecture for Libraries

The first step for supporting the New Architecture in your library is to ensure that it is compatible with the Interop Layer. The Interop Layer makes it possible to use existing native modules written for the legacy architecture with the New Architecture. You should do this before proceeding to convert your library to natively use TurboModules/Fabric. Starting with React Native 0.74, the Interop Layer is enabled by default and, if your library is compatible, then no changes will be needed on the user's side in order to use your library.

Using the Interop Layer

The Interop Layer will generally work out of the box with simple libraries, but the more complex your library is, the more likely it is that you will need to make some changes. The following sections will guide you through the process of verifying that your library works with the Interop Layer and fixing common issues that you encounter.

Important

If you have already added a Codegen spec to your library, but the library is not fully converted to TurboModules/Fabric, we recommend that you delete it before proceeding. You can add this back when you are ready to convert your library to natively use TurboModules/Fabric without the Interop Layer.

Test your library

Follow the guide for testing your library against the latest version of React Native. Be sure to test all of the functionality of your library to ensure that it works as expected.

Issues that you may encounter

[JavaScript] XYZ is not a function (it is undefined)

When accessing a module directly from NativeModules in the new architecture some operators won't work due to the fact that TurboModule uses object prototypes now, in order to lazily load methods. So, if you used an operator like the spread operator or a function like Object.keys on NativeModules.YourModule then you will need to change to use Object.create. Learn more in facebook/react-native#43221.

Spread operator example
// Before: spread operator worked, but it will not with interop
export default {
  ...NativeModules.RNCNetInfo,
  get eventEmitter(): NativeEventEmitter {
     ...
  }
}
// After: use Object.create instead
Object.create(NativeModules.RNCNetInfo, {
   eventEmitter: {
     get: () => {...},
     enumerable: true,
  },
})
Object.keys example
// Before: Object.keys worked, but it will not with interop
Object.keys(NativeModules.RNCNetInfo).forEach((key) => {
  ...
})
// After: use for ... in instead
for (const key in NativeModules.RNCNetInfo) {
  ...
}

[JavaScript] global.nativeCallSyncHook can't be used to detect legacy "Remote Debugging in Chrome" with JSC

global.nativeCallSyncHook === 'undefined' is a common way to check if you're running in "Remote Debugging in Chrome" (which is not supported with Hermes, only JSC). This is often used to provide some fallback behavior for sync native functions, because they do not work in the legacy remote debugging environment. Use "RN$Bridgeless" in global && RN$Bridgeless === true to determine if you are running in bridgeless. Learn more in LinusU/react-native-get-random-values#57.

It is generally not recommended to fork behavior based on whether bridgeless is enabled — this is an escape hatch that should be used sparingly.

Example "isRemoteDebuggingInChrome()" function
function isRemoteDebuggingInChrome () {
  // Remote debugging in Chrome is not supported in bridgeless
  if ('RN$Bridgeless' in global && RN$Bridgeless === true) {
    return false
  }

  return __DEV__ && typeof global.nativeCallSyncHook === 'undefined'
}

[iOS/Android] Interop Layer is not compatible with custom ShadowNodes

The Interop Layer doesn't work on either Android or iOS if a legacy view is specifying a custom ShadowNode, i.e. in Android by overriding the method getShadowNodeClass, createShadowNodeInstance etc. Fabric won't call those methods and the widget will most likely be rendered incorrectly (i.e. wrong size, 0 height so unclickable, etc.). You can either work around this by not using custom ShadowNode or by converting your library to use TurboModules/Fabric without the Interop Layer.

[Android] ThemedContext.getNativeModule() does not behave the same with Interop Layer

A native view manager used to have a ThemedContext, which is a class derived from ReactContext. It was common to call ThemedContext.getNativeModule() or other methods expected to be inherited from ReactContext. However, with the Interop Layer this will call to ReactContext's implementation (the CatalystInstance one) but not BridgelessReactContext. Accessing the internals to the CatalystInstance will cause an exception.

You can solve this by using ThemedContext.getReactApplicationContext().getNativeModule().

[iOS] Cannot access CallInvoker from "RCTCxxBridge.callInvoker"

In bridgeless mode, React Native does not support a fallback for jsCallInvoker, and the bridge.jsCallInvoker is nil. The app will crash when accessing null pointer. Instead, get callInvoker from getTurboModule:, e.g. shopify/react-native-skia#2223.

Get help

Search the "Libraries" discussion category for similar issues, and if you can't find a solution, post a discussion with details about the issue you are facing.

Update library status

Once you have verified that your library works with the Interop Layer, update the status of your library on reactnative.directory to indicate that it is compatible with the New Architecture. If your library is already listed there, set "newArchitecture": true in react-native-libraries.json, otherwise add your library to the directory.

Next steps

Once your library is compatible with the Interop Layer, release a new version for your users. You can proceed to converting your library to natively use TurboModules/Fabric when convenient for you.