0

I have a button that should trigger all windows to perform an action, play media.

I have a MediaProvider class in the main process that handles all communication for the media IPC channels (truncated for brevity). This class is initialized in main.ts.

export default class MediaProvider {

  public constructor() {

    ipcMain.on(MEDIA_CHANNELS.PLAY, async (e: Electron.IpcMainInvokeEvent) => {
      console.log('PLAY handler in ipcMain');
      const result = await this.playMedia();
      return result;
    });

    ...
  }

  private playMedia = () => {
    BrowserWindow.getAllWindows().forEach(window => {
      console.log(`Sending PLAY command to window ${window.id}.`)
      window.webContents.send(MEDIA_CHANNELS.PLAY);
    });
  }

  ...
}

My main component calls this on clicking the play button:

window.electron.ipcRenderer.send(MEDIA_CHANNELS.PLAY);

Which correctly triggers my ipcMain handler.

enter image description here

In another functional React component I have the following:

  useEffect(() => {
    initListeners();
  }, []);

  const initListeners = () => {
    window.electron.ipcRenderer.on(MEDIA_CHANNELS.PLAY, (_) => {
      console.log('PLAY in component');
      playMedia();
    });

    ...
  }

My preload.ts simply exposes ipcRenderer methods as-is:

contextBridge.exposeInMainWorld('electron', {
  ipcRenderer: {
    send: ipcRenderer.send,
    on: ipcRenderer.on,
    invoke: ipcRenderer.invoke
  },
});

The listener is never triggered, there is never a 'PLAY in component' log. What could be causing my listener to fail to trigger?

I tried rewriting the above component into a class component vs. functional, but it didn't fix anything. I also thought maybe the overlapping channel name between ipcMain and ipcRenderer was causing an issue, but I added a new channel name for testing and still could not get the listener to fire.

Tristan
  • 1,608
  • 1
  • 20
  • 34
  • The code you shared seems good, so the problem may come from somewhere else. Just be aware that your preload is wrong, exposing IPC API as-is is a security risk, as mentioned several time [in the docs](https://www.electronjs.org/docs/latest/tutorial/ipc). – Arkellys Jan 04 '23 at 06:05

1 Answers1

0

Since the asking of this question, the example preload.ts in the electron-react-boilerplate has been updated. https://github.com/electron-react-boilerplate/electron-react-boilerplate/blob/main/src/main/preload.ts

After matching the example for .on(...) exactly, and without altering anything else, my handlers now work.

...,
on(channel: MEDIA_CHANNELS, func: (...args: unknown[]) => void) {
  const subscription = (_event: IpcRendererEvent, ...args: unknown[]) =>
    func(...args);
  ipcRenderer.on(channel, subscription);

  return () => {
    ipcRenderer.removeListener(channel, subscription);
  };
},
...
Tristan
  • 1,608
  • 1
  • 20
  • 34