I'm working on a React Native plugin with a project structure that includes a test app within a demos
directory for testing the plugin functionalities.
My issue arises when trying to handle an event emitted from the native Android side. Despite setting up a NativeEventEmitter
in my plugin's index.js
, and adding a listener for the native event, the callback associated with the event does not seem to get triggered on the RN side.
Below is a simplified version of the implementation in my plugin's index.js
:
Now, in the demo app within the demos directory, I am able to successfully listen for and handle the same event:
const { myLibrary } = NativeModules;
const EventEmitter = new NativeEventEmitter(myLibrary);
useEffect(() => {
const validationSuccessListener = myLibConnector.onValidationResultSuccess(handleValidationSuccess);
const validationFailureListener = myLibConnector.onValidationResultFailure(handleValidationFailure);
// This works as expected when added directly in the demo app
const eventListener = EventEmitter.addListener(
'onValidationResultSuccess',
validationResult => {
console.log('>> Received onValidationResultSuccess Event');
console.log('validationResult', validationResult);
}
);
return () => {
validationSuccessListener();
validationFailureListener();
eventListener.remove();
};
}, []);
// Initialization of the Listener
private final MappedValidationResultListener listener = new MappedValidationResultListener() {
@Override
public void onFailure(String result, Throwable error) {
handleError(EVENT_VALIDATION_FAILURE, result, error);
}
@Override
public void onResponse(Map<String, Object> response) {
WritableMap writableMap = RNUtil.toWritableMap(response);
handleSuccess(EVENT_VALIDATION_SUCCESS, writableMap);
}
};
//HELPER METHODS
private void handleSuccess(String eventName, WritableMap response){
sendEvent(reactContext,eventName, response);
}
private void handleError(String eventName, String result, Throwable error) {
WritableMap resMap = Arguments.createMap();
resMap.putString("result", result);
resMap.putMap("error", error != null ? errorToMap(error) : null);
sendEvent(reactContext,eventName, resMap.toString());
}
private void sendEvent(ReactContext reactContext, String eventName, Object params) {
if (reactContext.hasActiveReactInstance()) { // Ensure the context is active
Log.d("ReactNativeJS", "Event: " + eventName + ", params: " + params.toString());
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
} else {
Log.d("ReactNativeJS", "Skipping event: " + eventName + " (CatalystInstance not active)");
}
}
So the listener within the demo app works perfectly, but when the same code is used through the plugin's index.js, the event listener callback is not triggered. I'm confident that the native event is being emitted correctly; however, it seems like there is some misconfiguration on the React Native side within the plugin.
Has anyone faced a similar issue or could provide insight into what might be going wrong here in the plugin setup? Any help would be greatly appreciated!
I saw sort of a similar here but not sure how to fix it on my end.
I tried to debug both on the native and the JS side and logged the output but the emitter on the plugin side didn't invoke at all , even with the most simple data I sent back to JS side. But when its on the Demo app it works good
I'd like to share a solution I came across for this problem where a native emitter wasn't firing events as expected when developing a React Native library.
In scenarios where a library and a demo app each have their own node_modules
, discrepancies between the instances of React Native can lead to events not being properly emitted or received. The root cause is that if a NativeEventEmitter
is created from a different instance of react-native
than the one your app is using, the listener functions won't be triggered.
To resolve this issue, ensure that both your library and demo app are using the NativeEventEmitter
from the same react-native
package. When testing locally, you can modify your import statements within your demo app as follows:
// Use this for testing locally in the demo app
import { NativeEventEmitter, NativeModules } from "demos/your-demo-app/node_modules/react-native";
Instead of:
// Avoid using this in your demo app for local tests
import { NativeEventEmitter, NativeModules } from "react-native";
Keep in mind that this adjustment is only for local testing purposes and should not be included in your production build. When you're ready to publish your library, revert back to the standard React Native imports.