React native UI is not getting rendered after callback from native event emitter. Even callback having state change

2.4k views Asked by At

I want to navigate the user to another screen in react native project after native app widget click in android. I was able to catch event using native event emitter in my MainView.js and there i changed state of one of my component and it got changed but UI is not getting rendered after this state change. It is showing blank screen and there is not error on the console. Thanks in advance for any help!!

export default class MainView extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: 'Hi, This is main screen for app widget!!!'};
  }
  componentDidMount() {
    const eventEmitter = new NativeEventEmitter();
    this.listener = eventEmitter.addListener('MyCustomEvent', (event) => {
      console.log('MyCustomEvent -->', event);
      console.log('MyCustomEvent ArticleId -->', event.ArticleId);
      if (event.ArticleId === data.articleId) {
              console.log('data ArticleId true', data.articleId);
        //navigation.push('Article Details', data);
        this.setState({
              text: data.articleDes,
            });
             // setText(data.articleDes);
              console.log('text -->', this.state.text);

          } else {
        //  setText('No such article found.');
        console.log('text -->', this.state.text);
      }
    });
  }
  componentWillUnmount() {
    this.eventListener.remove(); //Removes the listener
  }
  render() {
    return (
      <View style={{flex: 1}}>
        <Text>{this.state.text}</Text>
        <Button
          title="click"
          onPress={() => this.props.navigation.push('Article Details', data)}
        />
      </View>
    );
  }
}

CustomActivity source code which is launched from appwidget click. From this activity's oncreate, I'm emitting events to react-native main view.

int articleId = 0;
    if (getIntent() != null) {
        articleId = getIntent().getIntExtra("articleId", 0);
        Log.e("articleid", "" + articleId);
    }
   //  Put data to map
    WritableMap payload = Arguments.createMap();
    payload.putInt("ArticleId", articleId);

    // Emitting event from java code
    ReactContext context = getReactNativeHost().getReactInstanceManager().getCurrentReactContext();
    if ( context != null && context.hasActiveCatalystInstance()) {
        Log.e("react context", "not null");
        (getReactNativeHost().getReactInstanceManager().getCurrentReactContext())
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit("MyCustomEvent", payload);
    }
2

There are 2 answers

0
Cheezd On

This sound familiar with an issue I am experiencing on IOS. The code is similar, but I cannot guarantee that the underlying structure in Android works in the same way. Anyways, I am sending an event message from IOS-Native (written in swift in xCode) to React-native file using the NativeEventEmitter. After the initial render, the value just wont update, and as I understand this issue is not limited to this type of Event. After some googling I found out that everything you read from state inside that event-callback has a reference to only the first render, and will not update on future renders.

Solution; use useRef so you keep a reference to the the updated value. useRef keeps the value across renders and event-callbacks. This is not something I have found out myself, please look at https://medium.com/geographit/accessing-react-state-in-event-listeners-with-usestate-and-useref-hooks-8cceee73c559 and React useState hook event handler using initial state for, they are the one that deserves the credit.

4
LordKiz On

That is not how to use NativeEventEmitter. You need to initialise the NativeEventEmitter with the native module you are emitting events from:

import { NativeEventEmitter, NativeModules } from 'react-native';
const { myNativeModule } = NativeModules;

componentDidMount() {
    ...
    const eventEmitter = new NativeEventEmitter(myNativeModule);
    this.eventListener = eventEmitter.addListener('myEvent', (event) => {
       console.log(event.eventProperty) // "someValue"
    });
    ...
  }

  componentWillUnmount() {
    this.eventListener.remove(); //Removes the listener
  }

Read more about NativeModules here: https://reactnative.dev/docs/native-modules-android