'How to get a response for IPC Main from IPC Renderer
I know how to send message from Main to renderer process and also calling main from renderer. However, I am having trouble replying to main from renderer
Renderer
ipcRenderer.invoke('IPC_HANDLE', "ping").then((result) => {
console.log("from renderer", result)
})
Main.js
ipcMain.handle('IPC_HANDLE', async (event, args) => {
const data = args // ping
return "pong"
})
what I want to do now is send message from Main to Renderer and wait for a response. Is there a way to do this?
Solution 1:[1]
I was struggling with this as well a few days ago, and I actually got it to work, and what you need to do is expose the ipcRenderer in the preload script. so you would have something like this
Preload.js
const { ipcRenderer,contextBridge} = require("electron");
const ipc = ipcRenderer;
contextBridge.exposeInMainWorld('api', {
sendMessage: (args) => ipc.invoke('message',args),
})
renderer.js
btn = document.getElementById("send-submit");
btn.addEventListener("click", async ()=>{
const mes = "ping";
api.sendMessage(mes).then(data=>{
console.log(data);
});
main.js
ipc.handle('message', async (e,msg) => {
console.log(msg + " recieved");
return("pong");
})
this got it working for me, Hope it does for you too.
Solution 2:[2]
Currently there is no function similar to ipcRenderer.invoke() for the main process. IE: An individual function that sets up a channel to send a message to the render process and receive a message in response.
To do this, 2 individual channels need to be used. One to receive the main process message (window.ipcRenderer.on()) and within that, the second, to send a message back to the main process (window.ipcRenderer.send()) (on a different channel).
preload.js (main process)
This
preload.jsscript is only used to setup whitelisted channel names. All concrete implementations of functions these chanels call are setup within other main process scripts (not thepreload.jsscript). This seperates concerns and keeps things simple and organised.
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [
'render-to-main'
],
// From main to render.
'receive': [
'main-to-render'
],
// From render to main and back again.
'sendReceive': [
'render-to-main-to-render'
]
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
main.js (main process)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.webContents.send('main-to-render', 'Ping 1 (send from main process)'); })
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// ---
electronIpcMain.on('render-to-main', (event, message) => {
console.log(message); // Pong 1 (send from render process)
});
electronIpcMain.handle('render-to-main-to-render', (event, message) => {
console.log(message); // Ping 2 (invoke from render process)
return 'Pong 2 (handle from main process)';
});
index.html (render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
</head>
<body>
<label for="ping-main-process">Ping main process: </label>
<input type="button" id="ping-main-process" value="Ping 2">
</body>
<script>
// Receive message from main process.
window.ipcRender.receive('main-to-render', (result) => {
console.log(result); // Ping 1 (send from main process)
// Manually reply to main process (on a different channel)
window.ipcRender.send('render-to-main', 'Pong 1 (send from render process)');
});
// Invoke, sends message and waits for reply.
document.getElementById('ping-main-process').addEventListener('click', () => {
window.ipcRender.invoke('render-to-main-to-render', 'Ping 2 (invoke from render process)')
.then((result) => { console.log(result); }) // Pong 2 (handle from main process)
});
</script>
</html>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Andres Rodriguez |
| Solution 2 | midnight-coding |
