React Native新架构下Fabric组件Android/iOS事件处理实现方法
React Native Fabric 组件点击事件无法透传至JS侧修复方案
Android 端问题修复
核心问题共3处:spec文件类型导入错误、receiveEvent参数传值顺序错误、事件字段与spec定义不匹配,且Fabric架构下无需手动注册事件。
1. 修正spec类型定义
原代码中声明使用BubblingEventHandler但仅导入了DirectEventHandler,类型不匹配会导致codegen生成的事件绑定逻辑失效。点击事件如果不需要向父视图冒泡,直接使用DirectEventHandler即可,修正后代码:
import type {HostComponent, ViewProps} from 'react-native'; import type {DirectEventHandler} from 'react-native/Libraries/Types/CodegenTypes'; import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; type ClickEvent = Readonly<{ value: string; }>; interface NativeProps extends ViewProps { text: string; onClickHandler?: DirectEventHandler<ClickEvent>; } export default codegenNativeComponent<NativeProps>( 'MyButtonView', ) as HostComponent<NativeProps>;
Fabric架构下codegen会自动完成事件注册逻辑,必须删除ViewManager中重写的
getExportedCustomDirectEventTypeConstants方法,手动注册会覆盖自动生成的绑定配置,导致事件无法触发。
2. receiveEvent参数说明
RCTModernEventEmitter的receiveEvent方法各参数正确取值如下:
- 第一个int参数:渲染平面surfaceId,不是视图id,通过
UIManagerHelper.getSurfaceId(当前视图)获取 - 第二个int参数:目标视图的reactTag,直接调用视图本身的
getId()方法获取 - 第三个String参数:事件名,和spec中声明的事件属性名完全一致,此处填
"onClickHandler" - 第四个boolean参数:canCoalesceEvent事件合并开关,点击属于离散交互事件,传
false避免事件被合并丢失 - 第五个int参数:customCoalesceKey自定义合并key,无自定义合并逻辑传
0即可 - 第六个参数:事件数据Map,字段必须和spec中定义的完全匹配,原代码传
message字段和spec的value不匹配,JS侧无法读取 - 第七个int参数:category事件分类,普通点击事件传
1即可
3. 修正原生事件发送代码
import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.RCTModernEventEmitter; // 其他原有导入保持不变 public class MyButtonView extends androidx.appcompat.widget.AppCompatButton { public MyButtonView(Context context) { super(context); configureViews(); } private void configureViews(){ setBackgroundColor(Color.YELLOW); setOnClickListener(view -> { onReceiveNativeEvent(); }); } public void onReceiveNativeEvent() { WritableMap event = Arguments.createMap(); // 字段名必须和spec定义的value一致 event.putString("value", "MyMessage"); ReactContext reactContext = (ReactContext)getContext(); int surfaceId = UIManagerHelper.getSurfaceId(this); int viewTag = getId(); reactContext.getJSModule(RCTModernEventEmitter.class) .receiveEvent( surfaceId, viewTag, "onClickHandler", false, 0, event, 1 ); } }
更推荐使用Fabric标准的EventDispatcher方式发送事件,兼容性更好:
import com.facebook.react.uimanager.events.EventDispatcher; // 省略其他导入 public void onReceiveNativeEvent() { WritableMap event = Arguments.createMap(); event.putString("value", "MyMessage"); ReactContext reactContext = (ReactContext) getContext(); EventDispatcher dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); int surfaceId = UIManagerHelper.getSurfaceId(this); if (dispatcher != null) { dispatcher.dispatchEvent(new Event(surfaceId, getId()) { @Override public String getEventName() { return "onClickHandler"; } @Nullable @Override protected WritableMap getEventData() { return event; } }); } }
iOS 端 Fabric 组件事件实现
iOS端Fabric架构下事件不需要通过RCT_EXPORT_VIEW_PROPERTY手动导出,codegen会根据spec自动生成事件绑定逻辑,实现步骤如下:
- 执行codegen后会自动生成
MyButtonViewViewProtocol.h头文件,自定义组件直接遵守该协议即可,无需手动声明事件属性:
// MyButtonView.h #import <React/RCTViewComponentView.h> #import "MyButtonViewViewProtocol.h" NS_ASSUME_NONNULL_BEGIN @interface MyButtonView : RCTViewComponentView <MyButtonViewViewProtocol> @end NS_ASSUME_NONNULL_END
注意:Fabric组件必须继承
RCTViewComponentView,不能直接继承旧架构的UIView子类,否则事件绑定失效。
- 在点击回调中直接调用自动生成的事件block传值,字段保持和spec定义一致:
// MyButtonView.mm #import "MyButtonView.h" @implementation MyButtonView - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.backgroundColor = [UIColor yellowColor]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)]; [self addGestureRecognizer:tap]; } return self; } - (void)handleTap { if (self.onClickHandler) { self.onClickHandler(@{@"value": @"MyMessage"}); } } @end
JS侧使用验证
直接给组件传入对应事件属性即可收到原生回调:
import MyButtonView from './MyButtonView'; <MyButtonView text="测试按钮" style={{width: 120, height: 44}} onClickHandler={(e) => { console.log('收到原生点击事件:', e.nativeEvent.value); }} />
内容的提问来源于stack exchange,提问作者BraveEvidence
相关产品推荐
相关产品推荐

