I Have an Electron app with on the renderer process ReactJs and a Redux infra that connects, and syncs all the stores across all the open processes of the app (one main process and multiple renderer processes)
The Redux sync communication is implemented over electron IPCs, so I have a couple of listeners for different channels, these listeners are added per renderer process (browser window)
as an example, here is the code that listens to the dispatched actions from the main process:
export const listenToFromMainDispatchedActions = (store: Store, senderId: string): () => void => {
const listener = (event: IpcRendererEvent, args: any): void => {
if (!isValidAppAction(args)) {
appLogger.warn('Received an invalid action form main!!!');
} else {
const action: IAppReduxAction<any> = {
...args,
meta: {
...args.meta,
handler: ReduxActionHandler.RECEIVER,
},
};
if (action.meta.senderId !== senderId) store.dispatch(action);
}
};
ipcRenderer.on(IPCChannels.ACTION_DISPATCH, listener);
appLogger.warn('STORE_DISPATCH', 'ADDED ACTION_DISPATCH');
return (): void => {
ipcRenderer.removeListener(IPCChannels.ACTION_DISPATCH, listener);
appLogger.warn('STORE_DISPATCH', 'REMOVED ACTION_DISPATCH');
};
};
this piece of code is executed in my index.tsx
file like so:
// index.tsx
// ...
export const GLOBAL_SENDER_ID = uuidV4();
const store = storeCreator({
level: 'renderer',
reducer: renderersReducer,
sagas: mainAppWindowSagas,
senderId: GLOBAL_SENDER_ID,
});
const listenerRemover = listenToFromMainDispatchedActions(store, GLOBAL_SENDER_ID);
...
My Question is, How and when is the best way to call listenerRemover()
in order to remove the IPC listener?
I tried to catch different document/window events like beforeunload
and the likes but to no avail.
In order to keep everything "smooth", I actually open the needed windows at app start and hide them, closing windows hides them instead of killing them, I realize this is problematic events wise, but I need a way to gracefully remove these listeners, I didn't find anything helpful in the docs
Thanks in advance
So the solution I came up with is the following:
on the browser side/renderer process (in the index.tsx
) I do the following:
// index.tsx (renderer process)
// ...
export const GLOBAL_SENDER_ID = uuidV4();
const store = storeCreator({
level: 'renderer',
reducer: renderersReducer,
sagas: mainAppWindowSagas,
senderId: GLOBAL_SENDER_ID,
});
const listenerRemover = listenToFromMainDispatchedActions(store, GLOBAL_SENDER_ID);
const cleanup = (): void => {
appLogger.log('WINDOW CLOSING');
listenerRemover();
window.removeEventListener('beforeunload', cleanup);
}
window.addEventListener('beforeunload', cleanup)
...
and in the main process, I remove all the ipcMain counterparts:
// index.ts
import { app, ipcMain } from 'electron';
import main from './main'
// ....
app.whenReady()
.then(main)
.catch((error) => {
appLogger.error(TAG, error);
});
// .....
app.on('before-quit', () => {
Object.values(IPCChannels).forEach(
(channel) => {
ipcMain.removeAllListeners(channel);
}
);
appLogger.log('CLEANED ALL IPCS');
appLogger.log('Done, BYEBYE');
});
It seems to be working as expected and I manage to see the logs in the relevant places, although I'm not really sure on the validity of this approach, I will leave my question unanswered for a couple of days (today is 13.08.2020 12:15CEST).
If I wont get any other/better answers in a weeks time or so, I'll accept my answer as the correct one.