I am building a Music App where the mini player component holds the expo audio instance and it is separated from the bottom tab navigator for two important reasons :
1 - The audio instance lives in that mini-player component so navigation won't reset it.
2 - The mini player is global thus available in all application stack and tab navigations.
I am using NativeEventEmitter
to help me control the player actions (ie: Play/Pause;Next; Pervious;) since those function lives in that mini-player component and needs to be called from a stack screen inside the tab navigator ! .
Now in Android
it is working fine but in IOS
, it is causing the application to crash.
The error :
Native Module cannot be null
- node_modules/react-native/Libraries/EventEmitter/NativeEventEmitter.js:36:16 in App/Containers/Player/PlayerScreen.js:47:4 in _handleStop
The Logic I am implementing here , is upon clicking on the miniPlayer component after it is mounted, I am hiding it and navigating to the Player Screen
;
Player Screen :
...
class PlayerScreen extends Component {
constructor(props) {
super(props);
this.eventEmitter = new NativeEventEmitter();
this._handleNext = this._handleNext.bind(this);
this._handlePlayPause = this._handlePlayPause.bind(this);
this._handlePrevious = this._handlePrevious.bind(this);
this.state = {
time: "00:00"
};
}
componentDidMount() {
}
_handlePlayPause = () => {
this.eventEmitter.emit("PLAYPAUSE", "");
};
_handleNext = () => {
this.eventEmitter.emit("NEXTSONG", "");
};
_handlePrevious = () => {
this.eventEmitter.emit("PREVIOUSSONG", "");
};
_handleStop = () => {
this.eventEmitter.emit("STOPSONG", "");
};
...
render()
....
MiniPlayer Component :
componentDidMount() {
this.listener = DeviceEventEmitter.addListener("PLAYPAUSE", data => {
this._handlePlayAndPause();
console.log("New Event is registered At Listeners PLAY_PAUSE");
});
this.listenerN = DeviceEventEmitter.addListener("NEXTSONG", data => {
this._handleNextSong()
console.log("New Event is registered At Listeners NEXT_SONG ");
});
this.listenerP = DeviceEventEmitter.addListener("PREVIOUSSONG", data => {
this._handlePreviousSong()
console.log("New Event is registered At Listeners PREVIOUS_SONG");
});
this.listenerS = DeviceEventEmitter.addListener("STOPSONG", data => {
this._handleStopSong();
console.log("New Event is registered At Listeners STOPSONG");
})
According the some article on Medium:
React Native no longer include the Node Standard Library. However, there are standalone modules which have reimplemented the EventEmitter API.
While looking at the NativeEventEmitter.js
Abstract base class, I can see this at the constructor.
const EventEmitter = require('../vendor/emitter/EventEmitter');
const Platform = require('../Utilities/Platform');
const RCTDeviceEventEmitter = require('./RCTDeviceEventEmitter');
const invariant = require('invariant');
...
class NativeEventEmitter extends EventEmitter {
_nativeModule: ?NativeModule;
constructor(nativeModule: ?NativeModule) {
super(RCTDeviceEventEmitter.sharedSubscriber);
if (Platform.OS === 'ios') {
invariant(nativeModule, 'Native module cannot be null.'); <== This where it crashes
this._nativeModule = nativeModule;
}
}
Update :
I have created a snack with two component that reproduce what I am trying to achieve :
https://snack.expo.io/@git/github.com/oflarcade/customEventsExpo
on Android it is working fine / on IOS :