Electron Forge - Can't use ipcRenderer in the renderer file
Asked Answered
T

4

14

I just created a new application using the following command:

npx create-electron-app my-new-app --template=typescript-webpack

Inside the renderer.ts I added the following code

import "./index.css";
import { ipcRenderer } from "electron";

But when I run npm run start I have the following error in Browser Console

Uncaught ReferenceError: require is not defined

Update What I've tried:

webpack.plugins.js

const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
const webpack = require("webpack");
module.exports = [
  new ForkTsCheckerWebpackPlugin(),
  new webpack.ExternalsPlugin("commonjs", ["electron"]),
];

But it still doesn't work.

Terri answered 19/6, 2020 at 9:18 Comment(0)
T
40

Found Solution

The solution is to use ipcRenderer in a preload script.

preload.ts

import { ipcRenderer } from "electron";

index.ts

declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: any;

const mainWindow = new BrowserWindow({
  height: 600,
  width: 800,
  webPreferences: {
    preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
  },
});

package.json

  "plugins": [
    [
      "@electron-forge/plugin-webpack",
      {
        "mainConfig": "./webpack.main.config.js",
        "renderer": {
          "config": "./webpack.renderer.config.js",
          "entryPoints": [
            {
              "html": "./src/index.html",
              "js": "./src/renderer.ts",
              "name": "main_window",
              "preload": {
                "js": "./src/preload.ts"
              }
            }
          ]
        }
      }
    ]
  ]
Terri answered 19/6, 2020 at 10:52 Comment(5)
George, you may want to add " declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: any;" as a note for Typescript users or they'll get an error as the TS compiler will not know about this Global. This can be defined in the main index.ts or you can move these "declare" declarations including the default into "src/typings.d.ts" after creating a declare global { // your declare const....here }. If you wish I can edit this for you :)Diverge
How do you then reference ipcRenderer in the application? (a React component for example?)Coppock
@BlueWaters to send IPC message from React component, you must enhance the preload.ts script: import { contextBridge, ipcRenderer } from 'electron'; contextBridge.exposeInMainWorld('dog', { bark(arg1, arg2) { ipcRenderer.invoke('dog.bark', arg1, arg2); } }); Now there is a window.dog.bark() function available in your React component.Counseloratlaw
I'm close to implementing this solution. I've defined my function in the context bridge and I'm able to call it from the debug console like window.api.get(). However, in my react typescript component, I keep getting window.api is not a function. How do I declare the new function?Silvanasilvano
Hi @EvanLalo! If you use window.api I assume you have something like this in your preload script: contextBridge.exposeInMainWorld("api",{}) correct?Terri
A
6

The solution is using the ContextBridge API from electron for electron-forge react+webpack template

preload.js

import { ipcRenderer, contextBridge } from "electron";

contextBridge.exposeInMainWorld("electron", {
  notificationApi: {
    sendNotification(message) {
      ipcRenderer.send("notify", message);
    },
  },
  batteryApi: {},
  fileApi: {},
});

main.js

const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      worldSafeExecuteJavaScript: true,
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
    },
 });

ipcMain.on("notify", (_, message) => {
   new Notification({ title: "Notification", body: message }).show();
});

package.json

 "plugins": [
    [
      "@electron-forge/plugin-webpack",
      {
        "mainConfig": "./webpack.main.config.js",
        "renderer": {
          "config": "./webpack.renderer.config.js",
          "entryPoints": [
            {
              "html": "./src/index.html",
              "js": "./src/renderer.js",
              "name": "main_window",
              "preload": {
                "js": "./src/preload.js"
              }
            }
          ]
        }
      }
    ]
  ]

app.jsx

import * as React from "react";
import * as ReactDOM from "react-dom";

class App extends React.Component {
  componentDidMount() {
    electron.notificationApi.sendNotification("Finally!");
  }
  render() {
    return <h1>contextBridge</h1>;
  }
}

ReactDOM.render(<App />, document.body);
Amritsar answered 29/7, 2021 at 18:42 Comment(1)
this is exactly what i'm looking for.. thank you so much to answering this bro. appreciate itCocoon
I
0

If you are using typescript, you need to add this to your renderer.ts file:

declare global {
  interface Window {
    electron: {
      funcName: () => void;
    };
  }
}

Then you can use window.electron.funcName().

Itch answered 29/1, 2022 at 19:38 Comment(0)
E
-1

In index.js when creating new window use

win = new BrowserWindow({
        webPreferences: {
            preload: path.join(__dirname, 'app', 'assets', 'js', 'preloader.js'),
            nodeIntegration: true,
            contextIsolation: false
        },
    })

Simply add nodeIntegration: true to webPreferences.Then you would be able to use require

Enjoyment answered 19/6, 2020 at 9:52 Comment(2)
** nodeIntegration: true** is not recommended because of security reasonsTerri
Not to pile on here but @GeorgianStan is correct, best not to enable nodeIntegration. His above example is the correct answer.Diverge

© 2022 - 2024 — McMap. All rights reserved.