balthazar/react-native-zeroconf

EventEmitter memory leak?

drewvolz opened this issue · 4 comments

I mounted and unmounted the component that scans for devices a few dozen times and received this message.

(node) warning: possible EventEmitter memory leak detected. 11 listeners added.
Use emitter.setMaxListeners() to increase limit.

reactConsoleErrorHandler @ ExceptionsManager.js:71
console.error @ YellowBox.js:62
EventEmitter.addListener @ events.js:138
componentDidMount @ network-scan.js:41
proxiedComponentDidMount @ createPrototypeProxy.js:61
(anonymous) @ ReactCompositeComponent.js:353
measureLifeCyclePerf @ ReactCompositeComponent.js:85
(anonymous) @ ReactCompositeComponent.js:352
notifyAll @ CallbackQueue.js:73
close @ ReactNativeReconcileTransaction.js:36
closeAll @ Transaction.js:222
perform @ Transaction.js:163
perform @ Transaction.js:149
perform @ ReactUpdates.js:95
flushBatchedUpdates @ ReactUpdates.js:199
closeAll @ Transaction.js:222
perform @ Transaction.js:163
batchedUpdates @ ReactDefaultBatchingStrategy.js:65
batchedUpdates @ ReactUpdates.js:111
_receiveRootNodeIDEvent @ ReactNativeEventEmitter.js:126
receiveTouches @ ReactNativeEventEmitter.js:212
__callFunction @ MessageQueue.js:242
(anonymous) @ MessageQueue.js:108
guard @ MessageQueue.js:46
callFunctionReturnFlushedQueue @ MessageQueue.js:107
(anonymous) @ debuggerWorker.js:71

This might have happened because I am not making use of removeDeviceListeners. I did not see it as part of the documentation. Maybe the docs should receive an update.

Hi, thanks for the report!

Yes indeed, I forgot to update the readme, will do. Could you confirm it fixes your issues though?

Seems to have fixed it, thanks @apercu

I believe, this problem is not completely fixed. Here is why:

Since every new instance of the Zeroconf object starts with an empty this._dListeners in the constructor, if somehow that specific instance is lost during the application flow, there is no way to remove the previous listeners from a new instance of Zeroconf.

I escape this problem by manually call DeviceEventEmitter.removeAllListeners() before creating Zeroconf, like this:

import { DeviceEventEmitter } from "react-native";
import Zeroconf from "react-native-zeroconf";
/* important part */
DeviceEventEmitter.removeAllListeners("RNZeroconfStart");
DeviceEventEmitter.removeAllListeners("RNZeroconfStop");
DeviceEventEmitter.removeAllListeners("RNZeroconfError");
DeviceEventEmitter.removeAllListeners("RNZeroconfFound");
DeviceEventEmitter.removeAllListeners("RNZeroconfRemove");
DeviceEventEmitter.removeAllListeners("RNZeroconfResolved");
DeviceEventEmitter.removeAllListeners("RNZeroconfServiceRegistered");
DeviceEventEmitter.removeAllListeners("RNZeroconfServiceUnregistered");
/*  */
const zeroconf = new Zeroconf();

My suggestion is to do this in the removeDeviceListeners function (and maybe in the constructor) instead of calling remove on currently saved listeners in this._dListeners... not sure if it's the cleanest solution though.