0

As the title states, I would like to call a function defined in one of my React components from the Electron main.js file. I know how to do it the other way around, where for example the user clicks a button and it calls a function in main.js and then returns to the React component. I need it to be the opposite, where main.js calls the React function and then returns the result to main.

I cannot simply have all the functionality in main.js since I cannot pass DOM objects to main from React (it has resulted in the error "object cannot be cloned"). So I figure it shouldn't be too hard to just send the request to React, have the React side of my app do the stuff, and then return the resulting string back to main.

I have found a number of other similar posts here where various solutions are offered, but none specifically for this exact problem. #1 exposes ipcRenderer which is a bad security practice I can't afford. #2 and #3 only explain it for vanilla Javascript, not React components. And while #4 does deal with React, it does not deal with Electron, and it is designed for React classes (I'm using functional components).

My resulting attempt looks like this mess:

    const [mounted, setMounted] = useState(false)

    if(!mounted){
        window.reactFunction = myFunction;
    }
  
    useEffect(() =>{
      setMounted(true)
    },[])

    window.electron.receive("fromMain", async () => {
        return myFunction();
    });

    async function myFunction()
    {       
        return "result";
    }

However, the function instead gets called during rendering and is not called by the contextBridge. Here is my preload.js:

contextBridge.exposeInMainWorld('electron', {
 
    receive: async (channel, func) => {
        let validChannels = ["fromMain"];
        if (validChannels.includes(channel)) {
            return await ipcRenderer.on("fromMain", (event, ...args) => func(...args));
        }
    }
});

For some reason the preload.js script is not executed. And here is main.js:

       const result = window.webContents.send('fromMain');

I have also found this discussion where it is made clear the security issues associated with calling ipcRenderer directly and so I would like to use contextBridge (which I'm already using for calling main functions from React anyway). It's also where my preload.js code comes from. But that example doesn't use React either.

Is this even possible? Or am I supposed to go about this problem a different way?

1 Answer 1

0

Looks like I made a typing mistake on this line:

return await ipcRenderer.on("fromMain", (event, ...args) => func(...args));

In this line I'm returning the function, so the function itself is not getting called. Remove the "return" and it will work. "Await" doesn't seem to make a difference.

However, once you do call the function, it can't actually return anything. So you need to make another call from renderer back to main, like this:

    window.electron.receive("fromMain", async () => {
        const returnValue = await myFunction();
        window.electron.sendReturnValue(returnValue);
    });

    async function myFunction()
    {       
        return "result";
    }

Unless there is a still better way of doing it, but this solves my issue for now, and hopefully helps others.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.