Emisael Carrera
Emisael Carrera

Reputation: 687

How to Listen for Events in children components in React Native?

I want to implement an event listener from parents to children components in my React Native app, I'm using a StackNavigator as a router.

How can I listen for events occurred in the top/parent components?

Upvotes: 7

Views: 18808

Answers (6)

Otkelbay Akberdi
Otkelbay Akberdi

Reputation: 39

Simplest way:

1) Create listener at your app.js (Initial starting file): Example:

(new NativeEventEmitter()).addListener('loggedIn',() => {
        setAuth(true);
    });

2) Just emit from any child: (new NativeEventEmitter()).emit('loggedIn');

Hope it will be useful to someone!)

Upvotes: 2

SonKrishna
SonKrishna

Reputation: 189

I have followed OPs idea of using redux and it worked well for me. Here is the code...

actionTypes.js:

export const TRIGGER_REQUEST_SUBMIT = "TRIGGER_REQUEST_SUBMIT";
export const ACKNOWLEDGE_REQUEST_SUBMIT = "ACKNOWLEDGE_REQUEST_SUBMIT";

eventActions.js:

import * as types from "./actionTypes";

export function triggerRequestSubmit() {
  return function(dispatch) {
    dispatch({ type: types.TRIGGER_REQUEST_SUBMIT });
  };
}

export function acknowledgeRequestSubmit() {
  return function(dispatch) {
    dispatch({ type: types.ACKNOWLEDGE_REQUEST_SUBMIT });
  };
}

eventReducer.js:

import * as types from "../actions/actionTypes";
import initialState from "./initialState";

export default function eventReducer(state = initialState.event, action) {
  switch (action.type) {
    case types.TRIGGER_REQUEST_SUBMIT:
      return { ...state, shouldSubmitRequest: true };
    case types.ACKNOWLEDGE_REQUEST_SUBMIT:
      return { ...state, shouldSubmitRequest: false };
    default:
      return state;
  }
}

Don't forget to register reducer in your root-reducer/index.js in reducers

initialState.js:

export default {
  event: {
    shouldSubmitRequest: false
  }
};

Usage in child(Use redux to connect & get shouldSubmitRequest via state, acknowledgeRequestSubmit via dispatch):

 useEffect(() => {
    if (shouldSubmitRequest) {
      console.log("shouldSubmitRequest triggered in Request");
      acknowledgeRequestSubmit();
    }
  }, [shouldSubmitRequest]);

In parent call triggerRequestSubmit(); (connect to redux to get that method)

Upvotes: -1

Vishal Jadav
Vishal Jadav

Reputation: 984

Simply use React Native's DeviceEventEmitter.Emit your event from parent component like below:

DeviceEventEmitter.emit('eventKey', {name:'John', age:23});

and listen event in children component

componentDidMount(){
      //add listener
      this.eventListener = DeviceEventEmitter.addListener('eventKey',this.handleEvent);
}

handleEvent=(event)=>{
 //Do something with event object
}

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

Upvotes: 20

Emisael Carrera
Emisael Carrera

Reputation: 687

The best approach instead of using an EventEmitter with my current experience (7 months later this question) is using Redux as the state holder, you can create the variables that will pass the message through the components parent-children and then connect the component to get the variables (state) changes immediately it happens.

I decided to use Rematch that is a minified version of Redux and I have got very good results. It forced me to remove all my EventEmitters and use the dispatching methods to update variables (states) that will be updated in the connected components.

Upvotes: 2

ReyHaynes
ReyHaynes

Reputation: 3102

Having an Event Emitter can probably get a bit messy when tracking where the event came from or where it's being listened.

Just like you pass the event emitter down to the child via screenProps={this.eventEmitter} (which is what you are essentially doing), why not just pass an actual function your child can use to initiate actions on its parent. That'd probably be the most react way to do things.

It probably not is ideal to add an additional event listener as it will affect performance at some point.

Upvotes: -1

Emisael Carrera
Emisael Carrera

Reputation: 687

I've been researching in how to emit and listen events from a Parent component to a Child component in React Native, after trying different ways I think that it could be good to share how I have got it working.

In your parent component (App.js in my case), import the EventEmitter lib:

import EventEmitter from 'EventEmitter';

Create a variable instance of the Event Emitter:

export default class App extends Component {

...

  componentWillMount() {
    this.eventEmitter = new EventEmitter();
  }

...

}

Then, when something happens emit the event:

this.eventEmitter.emit('Event-Name', {args});

As I'm using a StackNavigator as a router I have to pass my eventEmitter variable to the children views:

const routes = {
  ChildView1: {
    screen: ChildComponent1
  },
  ChildView2: {
    screen: ChildComponent2
  },
  ...
};

const AppNavigator = StackNavigator(
  {
    ...routes,
  },
  {
    initialRouteName: 'ChildView1',
    headerMode: 'none',
  /*
   * Use modal on iOS because the card mode comes from the right,
   * which conflicts with the drawer example gesture
   */
    mode: Platform.OS === 'ios' ? 'modal' : 'card',
  }
);

/* Here is the magic for the children views */

render(){
  return (
    <AppNavigator screenProps={this.eventEmitter} />
  );
}

Ok, now we have available our event listener for the children views, let's make a call in one of our children components:

export default class ChildComponent1 extends Component {

  ComponentWillMount(){

    this.eventEmitter = this.props.screenProps;

    this.eventEmitter.addListener('Event-Name', ()=>{

      // Do something

    });

  }

  ...

}

This is how I have done my event listener for child component, I hope it helps someone else. If you have a better implementation please share/comment it.

Upvotes: 0

Related Questions