Створення React Native-бібліотеки з нативним модулем
React Native Community пропонує сотні готових бібліотек, але специфіка деяких завдань—проприєтарний SDK вендора, нестандартна робота з камерою або біометрією, інтеграція з корпоративною MDM-системою—потребує написання нативного модуля з нуля. Після появи New Architecture (JSI + TurboModules) процес кардинально змінився, і старі гайди по @ReactMethod більше не актуальні.
Old Architecture vs New Architecture: принципова різниця
Старша архітектура (Bridge) працює асинхронно через JSON серіалізацію. Виклик нативного методу: JavaScript → JSON серіалізація → Bridge queue → десеріалізація → Java/ObjC. Це додає ~1–5 мс на кожен виклик і робить неможливим синхронний доступ до нативного коду.
New Architecture використовує JSI (JavaScript Interface)—прямий C++ binding між JS-рушієм (Hermes) та нативним кодом. TurboModules завантажуються ліниво та викликаються синхронно. Для високочастотних операцій (кожен кадр анімації, real-time audio processing) це критично.
React Native 0.73+ включає New Architecture за замовчуванням. Бібліотеки повинні підтримувати обидві через codegen специфікацію.
Створення бібліотеки через create-react-native-library
npx create-react-native-library@latest my-module—стандартний scaffold. Генерує структуру:
my-module/
android/src/main/java/…/MyModule.kt
ios/MyModule.mm (Objective-C++ для JSI bridge)
src/index.tsx — TypeScript API
src/NativeMyModule.ts — codegen spec
Codegen специфікація
TypeScript-файл описує контракт, за яким codegen генерує C++ glue code:
// NativeMyModule.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
multiply(a: number, b: number): Promise<number>;
getDeviceId(): string; // синхронний метод—тільки в New Architecture
}
export default TurboModuleRegistry.getEnforcing<Spec>('MyModule');
getEnforcing викидає помилку при старті, якщо нативний модуль не зареєстрований—краще, ніж мовчаливий undefined.
Android-реалізація: Kotlin + ReactPackage
class MyModule(reactContext: ReactApplicationContext) :
NativeMyModuleSpec(reactContext) {
override fun getName() = NAME
override fun multiply(a: Double, b: Double): Promise<Double> {
return Promise.resolve(a * b)
}
override fun getDeviceId(): String {
return Settings.Secure.getString(
reactApplicationContext.contentResolver,
Settings.Secure.ANDROID_ID
)
}
companion object {
const val NAME = "MyModule"
}
}
NativeMyModuleSpec—абстрактний клас, сгенерований codegen з TypeScript специфікації. Якщо метод не реалізований—помилка компіляції, а не краш в рантаймі. Це ключова перевага New Architecture.
ReactPackage реєструє модуль:
class MyPackage : ReactPackage {
override fun createNativeModules(context: ReactApplicationContext) =
listOf(MyModule(context))
override fun createViewManagers(context: ReactApplicationContext) = emptyList<ViewManager<*, *>>()
}
iOS: Objective-C++ bridge
Для New Architecture iOS-реалізація пишеться на Objective-C++ (.mm) або Swift з ObjC-обгорткою. Swift нативно не підтримує JSI без bridge, тому .mm файл з #import <React/RCTUtils.h> залишається обов'язковим.
// MyModule.mm
#import "MyModule.h"
#import <React/RCTUtils.h>
@implementation MyModule
RCT_EXPORT_MODULE()
- (NSString *)getDeviceId {
return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
@end
Для синхронних методів в Old Architecture: RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD—працює, але блокує JS thread. У New Architecture синхронність через JSI не блокує JS thread—принципово інша модель.
Нативні View компоненти
Якщо завдання—кастомний нативний View (наприклад, SDK карт, кастомний відеоплеєр), використовуйте ViewManager на Android / RCTViewManager на iOS. New Architecture вводить Fabric для нативних компонентів—аналог TurboModules для віджетів. Codegen генерує ComponentDescriptor по TypeScript специфікації з codegenNativeComponent.
Підтримка Expo
Якщо додаток на Expo managed workflow—нативний модуль потребує Expo Modules API замість голого React Native. npx create-expo-module генерує правильний scaffold. ExpoModule реєструється автоматично без ReactPackage—Expo Autolinking знаходить модуль по package.json.
Типові помилки
-
Module not found у рантаймі—забули запустити
pod installна iOS після додавання модуля -
Mismatched types—TypeScript spec говорить
number, Kotlin приймаєDouble(ok), Swift приймаєInt(краш). Всі числа в JS—Doubleна нативній стороні -
Main thread violation—виклик UI-коду з нативного методу без dispatch на main thread:
DispatchQueue.main.async/UiThreadUtil.runOnUiThread
Розробка нативного модуля: простих операцій (1–3 методи)—3–5 днів. Складний модуль з EventEmitter, View-компонентами, підтримкою Old та New Architecture—3–5 тижнів. Вартість розраховується індивідуально.







