1

I have encountered strange behavior when using Electron's ipcRenderer with React's useEffect. Within my electron app, I have the following code:

import React, { useEffect } from 'react'
const electron = window.require('electron');
const ipcRenderer = electron.ipcRenderer;

...

const someValueThatChanges = props.someValue;

useEffect(() => {
  const myEventName = 'some-event-name';
  console.log(`Using effect. There are currently ${ipcRenderer.listenerCount(eventName)} listeners.`);
  console.log(`Value that has changed: ${someValueThatChanges}.`);
  ipcRenderer.addListener(myEventName, myEventHandler);
  console.log('Added a new listener.');
  // Should clean up the effect (remove the listener) when the effect is called again.
  return () => {
    ipcRenderer.removeListener(myEventName, myEventHandler)
    console.log('Cleaned up event handler.');
  }
}, [ someValueThatChanges ]);

function myEventHandler() {
  console.log('Handled event');
}

The code above is supposed to listen to the some-event-name event fired by Electron's main process with mainWindow.webContents.send('some-event-name'); and console.log(...) a message inicating that the event was handled.

This works as expected when the effect is initially run. A listener is added, the event is raised at a later time, and the string 'Handled event' is printed to to the console. But when the someValueThatChanges variable is assigned a different value and the event is raised for a second time, the 'Handled event' string is printed out to the console twice (the old listener does not appear to have been removed).

The line with the listenerCount(eventName) call returns 0 as expected when the removeListener(...) call is included in the useEffect return/cleanup function. When the removeListener(...) call is removed, the listenerCount(eventName) call returns a value that is incremented as expected (e.g. 0, 1, 2) as listeners are not removed.

Here's the really weird part. In either case, whether or not I include the call to removeListener(...), the myEventHandler function is always called for as many times as useEffect has been run. In other words, Electron reports that there are no event listeners, but myEventHandler still seems to be called by the previous listeners. Is this a bug in Electron, or am I missing something?

  • Maybe try with `on()` instead of `addListener()`? – Jeremy Harris Feb 10 '20 at 21:48
  • Thanks for the suggestion. I've tried both. According to the NodeJs docs which define `EventEmitter`, `on` and `off` are aliases for `addListener` and `removeListener` respectively: https://nodejs.org/api/events.html#events_emitter_addlistener_eventname_listener. Which brings up another weird thing: the Electron ipcRenderer API doesn't quite match the NodeJS EventEmitter API. Just enough to be annoying. –  Feb 10 '20 at 21:58
  • As an example, ipcRenderer supports `on()` but not `off()`, and `removeListener()` but not `addListener()`. The NodeJS API supports all four. That said, neither of the four functions, when used on `IpcRenderer`, throw any errors. So either `off` and `addListener` are supported in Electron but not documented, or they aren't supported and fail silently. –  Feb 10 '20 at 22:01
  • I've created a feature request on the Electron repo to standardize the ipcRenderer API/update its documentation. My issue here remains unresolved, but for those who are interested: https://github.com/electron/electron/issues/22129 –  Feb 10 '20 at 22:29

1 Answers1

1

Never try with ipcRenderer.addListener, But try ipcRenderer.on instead

useEffect(() => {
  ipcRenderer.send('send-command', 'ping');

  ipcRenderer.on('get-command', (event, data) => {
    console.log('data', data);
  });

  return () => {
    ipcRenderer.removeAllListeners('get-command');
  };
}, []);

I believe, the docs changed. ipcRenderer.removeAllListeners accept single string instead of array of string Source electron issues,

Wachid
  • 447
  • 5
  • 8