Создание React Native-библиотеки с нативным модулем
React Native Community предлагает сотни готовых библиотек, но специфика некоторых задач — проприетарный SDK вендора, нестандартная работа с камерой или биометрией, интеграция с корпоративной MDM-системой — требует написания нативного модуля с нуля. После появления New Architecture (JSI + TurboModules) процесс кардинально изменился, и старые гайды по @ReactMethod уже не актуальны.
Old Architecture vs New Architecture: принципиальная разница
Старая архитектура (Bridge) работает асинхронно через сериализацию JSON. Вызов нативного метода: JavaScript → JSON serialization → Bridge queue → deserialization → 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 недель. Стоимость рассчитывается индивидуально.







