Search code examples
androidreact-nativemobilepluginsnativeeventemitter

React Native Plugin Not Receiving Events from Native Event Emitter in index.js but Works in Demo App


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


Solution

  • 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.