ElectronJS: Uncaught TypeError: ipcRenderer.on is not a function

Viewed 686

Using: Electron v15.2.0 using the Electron Quick Start

I know this is a very simple problem, but I can't seem to find a solution for it. Here's the code.

main.js:

// Get Tasks
ipcMain.on('json:get', (event, dataset) => {

  // Read the JSON file
  fs.readFile('./data/data.json', 'utf8', (err, jsonString) => {

    // Throw error to console if there is one
    if (err) {
      console.log("File read failed:", err)
      return
    }

    console.log(`JSON Data: ${jsonString}`)

    // Send returned JSON data to mainWindow
    mainWindow.webContents.send('tasks.return', jsonString)

  })
})

preload.js:

const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('ipcRenderer', ipcRenderer)

renderer.js

document.getElementById('test').addEventListener('click', () => {
    ipcRenderer.send('json:get', 'tasks')
})

ipcRenderer.on('tasks.return', (event, json) => {
    console.log(json)
})

What's happening is that the event listener is firing correctly, I know that because the JSON will dump into the console from the main.js code, but it's not firing from the renderer.js code. And I'm getting the error: Uncaught TypeError: ipcRenderer.on is not a function, but it's being called on:

ipcRenderer.on('tasks.return', (event, json) => {
    console.log(json)
})

But, it's not being called with the event listener fires.

I tried adding:

const electron = require('electron')
const { ipcRenderer } = electron

To renderer.js, but then I get this error: Uncaught SyntaxError: Identifier 'ipcRenderer' has already been declared and neither work.

I know that I need to shorten the scope of the contextBridge and ipcRenderer in the preload.js file, and I will, but the issue is that I can't get the Electron app to send messages back to the mainWindow.

Thanks in advance!

1 Answers

I believe you're seeing this issue. ipcRenderer does not have an on method when in a (context-isolation-enabled?) renderer.

The solution is to move the ipcRenderer.on logic inside of the preload script like so (full code example here):

contextBridge.exposeInMainWorld("api", {
    onResponse: (fn) => {
        ipcRenderer.on("tasks.return", (event, ...args) => fn(...args));
    }
});

And then your renderer can do:

window.api.onResponse((json) => console.log(json));

(keep in mind the memory leak for which a solution is mentioned here)

Related