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.
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.
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);
};
},
...