React-native: No documentation on using DeviceEventEmitter

Created on 17 Sep 2015  路  35Comments  路  Source: facebook/react-native

After the release of 0.11.0 I migrated from react-native-keyboard-event to the built-in events but didn't find any documentation on how to use them. At all. After digging through the code for a while I found out how to add and (after some more digging) how to remove listeners. But there surely should be some documentation on that. If you provide a starting point I'd contribute a couple of paragraphs.

Locked

Most helpful comment

I have looked at those files to understand whats going on. But firstly I feel that this is an important enough subject to get covered in the main react-native docs, probably in the API section. Secondly it wasn't obvious to me at first how to remove the one listener again after registering it. The API only provides removeAllListeners and removeCurrentListener, both not covering what I want to do. My current solution looks as follows:

var React = require('react-native')
    , DeviceEventEmitter = React.DeviceEventEmitter
    , _keyboardWillShowSubscription
    , _keyboardWillHideSubscription
    ;

var component = React.createClass({

    componentDidMount: function() {
        _keyboardWillShowSubscription = DeviceEventEmitter.addListener('keyboardWillShow', (e) => console.log('keyboardWillShow', e));
        _keyboardWillHideSubscription = DeviceEventEmitter.addListener('keyboardWillHide', (e) => console.log('keyboardWillHide', e));
    },

    componentWillUnmount: function() {
        _keyboardWillShowSubscription.remove();
        _keyboardWillHideSubscription.remove();
    },

    //...
});

All 35 comments

Can you share your code please?

I have looked at those files to understand whats going on. But firstly I feel that this is an important enough subject to get covered in the main react-native docs, probably in the API section. Secondly it wasn't obvious to me at first how to remove the one listener again after registering it. The API only provides removeAllListeners and removeCurrentListener, both not covering what I want to do. My current solution looks as follows:

var React = require('react-native')
    , DeviceEventEmitter = React.DeviceEventEmitter
    , _keyboardWillShowSubscription
    , _keyboardWillHideSubscription
    ;

var component = React.createClass({

    componentDidMount: function() {
        _keyboardWillShowSubscription = DeviceEventEmitter.addListener('keyboardWillShow', (e) => console.log('keyboardWillShow', e));
        _keyboardWillHideSubscription = DeviceEventEmitter.addListener('keyboardWillHide', (e) => console.log('keyboardWillHide', e));
    },

    componentWillUnmount: function() {
        _keyboardWillShowSubscription.remove();
        _keyboardWillHideSubscription.remove();
    },

    //...
});

Hm. Maybe something like the Reflux.listenTo mixin would be useful here.

Thanks for the example! I'm not very familiar with this API but just searched the fb codebase and I indeed found:

RCTDeviceEventEmitter.addListener(
  'keyboardWillShow',
  this.handleKeyboardWillShow
);

RCTDeviceEventEmitter.addListener(
  'keyboardWillHide',
  this.handleKeyboardWillHide
);

Handling keyboard show and hide events indeed does seem like an important topic to cover. Would you be up for sending a PR with documentation?

Here is the implementation showing all the available keyboard events: https://github.com/facebook/react-native/blob/master/React/Base/RCTKeyboardObserver.m

Looks like these events are not implemented on Android yet.

Sure, I can try to spin up a little bit of documentation, but where do I put it? Just create a new file in the docs folder? Also I'm not sure if my example above with local variable inside the component really is best practice.

I believe DeviceEventEmitter should get it's own section under APIs will all the events listed (description would be handy). There are plenty of events being sent during the app lifecycle, including these handy keyboard events.

I'm trying to get this to work with no luck. Following the examples above, I tried to attach a listener like so:

  componentWillMount: function() {
    DeviceEventEmitter.addListener('keyboardWillShow', function(e: Event) {
      console.log('keyboard opened');
    });
  },

I took those lines directly from the docs here:
https://facebook.github.io/react-native/docs/native-modules-android.html

While also declaring the necessary variable at the top of my fiel:

let React = require('react-native');

let {
  View,
  Text,
  ScrollView,
  StyleSheet,
  TouchableHighlight,
  TouchableOpacity,
  Image,
  TextInput,
  Animated,
  DeviceEventEmitter
} = React;

But I get nothing in my console upon opening the keyboard. Any idea?

@nicholasalanbrown I can't tell off the top of my head what's wrong with the code but I'd recommend using Stack Overflow for asking question. A lot of people from the community hang out there and someone might know.

Searching the RN codebase for keyboardWillShow. In ScrollResponder.js:

mixins: [Subscribable.Mixin],
...
componentWillMount: function() {
  this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', this.scrollResponderKeyboardWillShow);

@dvine-multimedia @grabbou Yes, creating a new file in /docs and adding it under APIs would be awesome.

I still think a section in the documentation would be nice but this one was totally my fault. The boilerplate I started with was using 0.10.0. Sorry!

@mkonicek Your mixin-based example is definitely the way to go here.
I vote for that in the docs rather than the _keyboardWillShowSubscription.remove(); method!
If there are two instances of the component things will break, and it's more fiddly.

Here's my example:

lang=javascript const Subscribable = require('Subscribable'); ... const LoginPage = React.createClass({ mixins: [Subscribable.Mixin,], componentWillMount () { // Use mixin so the listener is removed again this.addListenerOn(DeviceEventEmitter, 'keyboardWillShow', this.keyboardWillShow, this); this.addListenerOn(DeviceEventEmitter, 'keyboardWillHide', this.keyboardWillHide, this); }

Nice! Can you send a PR for the docs please?

Great find. I found an example of the use of Subscribable with the DeviceEventEmitter in the Android section, but it's actually missing the import statement and it's not covering the specific keyboard use case.
https://facebook.github.io/react-native/docs/native-modules-android.html#sending-events-to-javascript

Also maybe explain the difference with NativeAppEventEmitter (looks like it's just the naming - files are the same), as it looks like iOS docs are referring to that one.

@grabbou Your right, its just the naming thats different.
Here is a reference to the commit if anyone else is interested. https://github.com/facebook/react-native/commit/c668fd5be03438f3b7fb8a85c6eb778115f60f9c

Just stumbled upon this issue. The solution by @mkonicek works, however as of RN 0.27 the yellow box pops in:

`keyboardWillShow` event should be registered via the Keyboard module
`keyboardWillHide` event should be registered via the Keyboard module

The above sample has to be slightly modified:

const Keyboard = require('Keyboard');
const Subscribable = require('Subscribable');

const MyComponent = React.createClass({
  mixins: [Subscribable.Mixin,],
  componentWillMount() {
      this.addListenerOn(Keyboard, 'keyboardWillShow', this.keyboardWillShow, this);
      this.addListenerOn(Keyboard, 'keyboardWillHide', this.keyboardWillHide, this);
  }

Make sure to require the Keyboard module instead of import because the latter doesn't work... anyone knows why?

PS: Official docs would be really helpful.

I think require check for @provideModules as well

Coming across the same issue here as well. Everything seems to be setup fine with the new Keyboard component so I'm not sure by importing it doesn't work. Will try and dig deeper later today.
/cc @nicklockwood Any ideas?

import Keyboard from 'Keyboard';

works fine for me in 0.27.1. But how would I use it with ES2015 classes?

with 0.26 I had something like:

this._keyboardDidShowSubscription = DeviceEventEmitter.addListener(
  'keyboardWillShow', e => this._keyboardDidShow(e)
);

Oh, you're right @knowbody. I tried import {Keyboard} from 'react-native' but that doesn't work.

This is quite confusing because Keyboard.js is located in react-native/Libraries/Components/Keyboard just like other components which are exposed via react-native.

Sorry, I didn't see the latest release notes for 0.27.1. I was still on rc.

yep, it's not exported. I am releasing the patch version in a bit.

cc @JoelMarcey, could be interesting to look at how the docs could be improved here.

For those landing here, here's a clean way to do things following patch @grabbou's mentioned:

import React, { Component } from 'react'
import { Keyboard } from 'react-native'

class NowListenHereSee extends Component {
  componentDidMount() {
    // Attach listeners on mount
    Keyboard.addListener('keyboardDidShow', (e) => console.log('keyboardDidShow', e))
    Keyboard.addListener('keyboardDidHide', (e) => console.log('keyboardDidShow', e))
  }

  componentWillUnmount() {
    // Remove listeners on unmount
    Keyboard.removeListener('keyboardDidShow', (message) => console.log(message))
    Keyboard.removeListener('keyboardDidHide', (message) => console.log(message))
  }
}

export default NowListenHereSee

Borrows @dvine-multimedia's earlier suggestion on removing listeners to help avoid memory leaks, uses Did instead of Will (for Android support) and cleans up the syntax a bit. If I botched anything just let me know and I'll update the example as needed to serve as living documentation.

I was going to just add docs for this by adding the file to the whitelist. But the code in Libraries/EventEmitter/RCTDeviceEventEmitter.js seems to claim that DeviceEventEmitter is actually deprecated. So it is not clear to me whether the right thing is to fix the docs telling people to use DeviceEventEmitter, or to document it and un-deprecate it. So I am stumped and will wander off to other issues....

I'm going to remove the confusing bit of documentation that refers to a deprecated API. Seems like the main use case for this has been resolved by the Keyboard API, anyway.

Hi, what is the proper way to remove the Keyboard listener?
Is it:
this.keyboardDidShowListener.remove();
or as mentioned by @jhabdas @grabbou:
Keyboard.removeListener('keyboardDidShow', (message) => console.log(message));

???

Also,
https://facebook.github.io/react-native/docs/keyboard.html
^ @jhabdas Is right here... should use componentDidMount() instead.

@hramos The Android native modules docs still mentions sending "events" and says more information will be below, but this documentation has now been removed. What are we to use for event emitting besides RCTDeviceEventEmitter now for Android?

@booboothefool just saw your comment. think the edit may have not triggered an email. not sure. or GH or my inbox are totes overloaded. xD thanks for the nfo

Should be restored in #14143

@hramos What is the state of this? It looks like #14143 was closed and not merged. The docs still have no reference to DeviceEventEmitter. It is currently not at all clear on the right way to send events from Java to Javascript.

Note that there are more use cases than just Keyboard for this emitter. In our case, we receive data via a WebRTC data channel (in Java-land) and emit to pass the data into our global state manager (in React-land). If DeviceEventEmitter is deprecated and goes away, is there a replacement event bus to use? If so, where is it documented? I wouldn't think Keyboard would be the right thing to use.

As a side note, the reason I'm here is that I'm having troubles with our event-passing implementation. We upgraded from 0.43 to 0.44 a while back. I've just discovered that when I build a release APK, events emitted from Java are received by my javascript global store (mobx-based). But when I build a debug APK, the Java code calls emit, but the event is never received in JS. This is with the exact same codebase, just building debug vs release.

While trying to figure out why this would happen, I discovered that the 0.43 docs include DeviceEventEmitter info but 0.44 does not. Which led me to here. And now I'm even more confused. Is it still supported or not? If not, what do we use instead?

UPDATE:

I was able to resolve my release vs debug issue and now have events flowing from java -> react-native using NativeAppEventEmitter. This was a bug in my code, not a react-native issue.

I still think that the documentation needs more clarity on sending events. It would be great to include information on the intent and usage of each EventEmitter (EventEmitter, NativeEventEmitter, NativeAppEventEmitter, DeviceEventEmitter, etc.) as well as which are deprecated. A bit of history and reasoning behind the changes would also be quite valuable.

I just discovered RCTNativeAppEventEmitter (java) and NativeAppEventEmitter (js). These sound like better candidates for my use case (general app-specific messages vs device-specific). However, after updating the code to use it (following this example implementation), events are still not delivered in a debug build.

Was this page helpful?
0 / 5 - 0 ratings