Abel Callejo
Abel Callejo

Reputation: 14929

ipcRenderer.on is not a function

I am using the MainPreloadRenderer process stack.

preload.js

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

renderer.js

console.log(ipcRenderer) // it shows something like:
/**
{_events: {…}, _eventsCount: 0, _maxListeners: undefined, send: ƒ, sendSync: ƒ, …}
invoke: ƒ ()
postMessage: ƒ ()
send: ƒ ()
sendSync: ƒ ()
sendTo: ƒ ()
sendToHost: ƒ ()
Symbol(kCapture): false
_events: {}
_eventsCount: 0
_maxListeners: undefined
__proto__: Object
**/

console.log(ipcRenderer.on) // undefined

When trying to use the ipcRenderer.on() method, it is logging an error:

Uncaught TypeError: ipcRenderer.on is not a function

Was this method been removed in the newer versions? What is causing it to be not available on the renderer process?

Upvotes: 8

Views: 8629

Answers (4)

cooper
cooper

Reputation: 21

this is a nice way to resolver this problem.

const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("electron", {
  ipcRenderer: {
    ...ipcRenderer,
    on: ipcRenderer.on.bind(ipcRenderer),
    removeListener: ipcRenderer.removeListener.bind(ipcRenderer),
  },
});

Upvotes: 2

Jonas Both
Jonas Both

Reputation: 61

Here's a little workaround for that. Yet i don't think this is a good idea for security.

const { contextBridge, ipcRenderer } = require("electron");    
contextBridge.exposeInMainWorld("electron", { ipcRenderer: { ...ipcRenderer, on: ipcRenderer.on } });

Upvotes: 6

tuqiang
tuqiang

Reputation: 1

Here's a little workaround for that.

// preload.ts
contextBridge.exposeInMainWorld('_Electron_Event_Listener_', {
    onCreateBrowserView,
})

function onCreateBrowserView(listener: (event: IpcRendererEvent, ...args: any[]) => void) {
    ipcRenderer.removeAllListeners('/browserView/create')
    ipcRenderer.on('/browserView/create', listener)
}

Upvotes: 0

midnight-coding
midnight-coding

Reputation: 3217

As the Electron API documentation for ipcRenderer states, "The ipcRenderer module is an EventEmitter".

This "EventEmitter" referenced by Electron is the EventEmitter class of node.js. Electron's ipcRenderer module inherits node's EventEmitter class.

The methods viewable by console.log(ipcRenderer) are Electron's methods. All inherited methods are found in the Prototype object, which is exactly where you will find the on() method.

To see this in action add console.log(ipcRenderer) to a typical, well formed, safe and readable preload.js script.

preload.js (main thread)

// 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': [],
        // From main to render.
        'receive': [],
        // From render to main and back again.
        'sendReceive': []
    }
};

// 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)) {

                // Show me the prototype (use DevTools in the render thread)
                console.log(ipcRenderer);

                // 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);
            }
        }
    }
);

Now, open Chrome's DevTools and view the Console tab.

Chrome DevTools - Console Tab Output (render thread)

v EventEmitter
  > invoke: async ƒ (e,...t)
  > postMessage: ƒ (e,t,n)
  > send: ƒ (e,...t)
  > sendSync: ƒ (e,...t)
  > sendTo: ƒ (e,t,...n)
  > sendToHost: ƒ (e,...t)
  > _events: {app:versions: ƒ}
    _eventsCount: 1
    _maxListeners: undefined
    Symbol(kCapture): false
  v [[Prototype]]: Object
    > addListener: ƒ addListener(type, listener)
    > emit: ƒ emit(type, ...args)
    > eventNames: ƒ eventNames()
    > getMaxListeners: ƒ getMaxListeners()
    > listenerCount: ƒ listenerCount(type)
    > listeners: ƒ listeners(type)
    > off: ƒ removeListener(type, listener)
    v on: ƒ addListener(type, listener)        // <-- Here it is
        length: 2
        name: "addListener"
      > prototype: {constructor: ƒ}
        arguments: (...)
        caller: (...)
        [[FunctionLocation]]: VM14 node:events:486
        [[Prototype]]: ƒ ()
        [[Scopes]]: Scopes[2]
    > once: ƒ once(type, listener)
    > prependListener: ƒ prependListener(type, listener)
    > prependOnceListener: ƒ prependOnceListener(type, listener)
    > rawListeners: ƒ rawListeners(type)
    > removeAllListeners: ƒ removeAllListeners(type)
    > removeListener: ƒ removeListener(type, listener)
    > setMaxListeners: ƒ setMaxListeners(n)
      _events: undefined
      _eventsCount: 0
      _maxListeners: undefined
    > constructor: ƒ EventEmitter(opts)
      Symbol(kCapture): false
    > [[Prototype]]: Object

Upvotes: 4

Related Questions