React Native iOS端NativeEventEmitter报addListener不存在错误
问题根因
这个this._nativeModule.addListener is not a function报错仅触发在iOS端,核心原因是React Native iOS平台的NativeEventEmitter初始化逻辑存在强制校验:传入的原生模块必须继承自RCTEventEmitter,并且向JS层暴露addListener、removeListeners两个桥接方法,缺少任意一个就会直接抛出该错误。
Android端事件派发机制不做这个前置校验,只要模块能被NativeModules找到就能正常绑定监听,所以会出现Android运行正常、iOS实例化就报错的现象。
另外你贴的JS代码本身存在逻辑冗余:构造函数接收了module入参,但实际初始化NativeEventEmitter时用的是全局导入的TimJSModule,入参完全没被使用。
可行解决方案
按以下步骤修改即可解决问题:
- 修正iOS端原生模块实现
找到iOS目录下TimJs模块的原生代码,把模块的父类从普通的RCTBridgeModule/NSObject改成RCTEventEmitter,同时必须导出addListener、removeListeners两个方法,还要在supportedEvents里声明所有你要发往JS层的事件名。
如果是Swift实现的原生模块,参考代码如下:
import React @objc(TimJs) class TimJs: RCTEventEmitter { private var hasListeners = false override func supportedEvents() -> [String]! { return ["logFromNative", "sdkListener"] } @objc override func addListener(_ eventName: String!) { super.addListener(eventName) hasListeners = true } @objc override func removeListeners(_ count: Double) { super.removeListeners(count) let currentListenerCount = bridge.value(forKey: "listenerCount") as? Int ?? 0 hasListeners = currentListenerCount > 0 } // 原有业务方法保留在下方即可 }
如果是Objective-C实现的原生模块,参考代码如下:
#import <React/RCTEventEmitter.h> @interface RCT_EXTERN_MODULE(TimJs, RCTEventEmitter) RCT_EXTERN_METHOD(addListener:(NSString *)eventName) RCT_EXTERN_METHOD(removeListeners:(double)count) // 原有业务方法的声明保留在下方即可 @end
注意:原生端发送事件时,先判断
hasListeners为true再调用sendEvent方法,避免无监听时触发不必要的性能损耗。
- 修正JS层代码逻辑
把原来冗余的入参逻辑理顺,同时保存监听订阅实例,增加销毁方法避免内存泄漏,修正后代码如下:
import { NativeEventEmitter, NativeModules, EmitterSubscription } from "react-native"; const TimJSModule = NativeModules.TimJs; export class ExampleClass { private manager: String = "sdkManager"; private nativeModule: any; private logEmitterSubscription?: EmitterSubscription; private sdkEmitterSubscription?: EmitterSubscription; constructor(module?: any) { this.nativeModule = module ?? TimJSModule; this.addListener(); } private addListener() { const eventEmitter = new NativeEventEmitter(this.nativeModule); this.logEmitterSubscription = this.addLogListener(eventEmitter); this.sdkEmitterSubscription = this.addSDKListener(eventEmitter); } private addLogListener(eventEmitter: NativeEventEmitter) { return eventEmitter.addListener('logFromNative', (data: any) => { const { logs } = data; console.log("======log from native module======", logs); }); } private addSDKListener(eventEmitter: NativeEventEmitter) { return eventEmitter.addListener('sdkListener', (response: any) => { // 自定义事件处理逻辑 }); } // 实例销毁时必须调用该方法移除监听 destroy() { this.logEmitterSubscription?.remove(); this.sdkEmitterSubscription?.remove(); } }
- 清理缓存重新编译
原生代码修改后必须清理缓存重新编译,否则改动不会生效,依次执行以下命令:
# 清理Metro缓存 npx react-native start --reset-cache # 重新安装iOS依赖 cd ios && pod install && cd .. # 重新运行iOS项目 npx react-native run-ios
不要尝试在JS层手动给原生模块挂载addListener的mock方法,这种方式会导致iOS端原生事件无法正常传递到JS层,属于无效的临时规避方案
内容的提问来源于stack exchange,提问作者joy
相关产品推荐
相关产品推荐

