skip to Main Content

I have an Electron app (electron v23) that reads a config file and determines some configurations. I have a separate react project, my Electron window opens the URL for the react app once the configuration has been decided.

The Electron app needs to put the config object in the React app’s window object (renderer’s window object) before any of the client code can execute.

I tried a few methods, but none seem to work:

Approach 1:

const validChannels = ['something'];
const { contextBridge, ipcRenderer } = require("electron");
const { getConfig } = require("./config");

contextBridge.exposeInMainWorld("electron", {
    getConfig: () => {
        return getConfig();
    },
    send: (channel, data) => {
        if (validChannels.includes(channel)) {
            ipcRenderer.send(channel, data);
        }
    },
    receive: (channel, func) => {
        if (validChannels.includes(channel)) {
            // Deliberately strip event as it includes `sender` and is a security risk
            ipcRenderer.on(channel, (event, ...args) => func(...args));
        }
    },
});

Here is how I am opening the window for the same:

const win = new BrowserWindow({
    width: 1600,
    height: 1000,
    webPreferences: {
        nodeIntegration: false,
        devTools: true,
        contextIsolation: true,
        sandbox: false,
            preload: path.join(__dirname, "preload.js"),
        }
    }
});

When I use this approach I get an error at some internal script electron.app.isPackaged cannot be accessed as electron.app is undefined. I tried a few other options like setting nodeIntegration: true but nothing worked so far.

Approach 2

win.webContents.on("did-finish-load", () => {
    win.webContents.executeJavaScript(`window.__configFromElectron = ${JSON.stringify(getConfig())};`);
});

This approach does set the variable __configFromElectron but this is set much later in the code. The initialization scripts in React that need this config receive an undefined, but much later they get the actual config. I tried different options here like did-start-loading and dom-ready.None of them make it so that the variable is loaded first.

I tried a few other approaches where I try to block the React render of components until this variable is available, but this config is expected in a singleton and it is getting evaluated beforehand, even if I block the render process. (I used methods like ipcMain.handle and ipcRenderer.invoke) but none of these solutions are elegant.

Doing a refactor of the React app such that this config is async and all the dependencies have to await is a very big change. What is the right way to go about this?

2

Answers


  1. You can do something like this in the your Approach 2.

    You need to ensure that the config object is set before any of the code that depends on it runs. You could use a Promise to ensure that the config object is set before other code executes.

    let configPromise;
    
    win.webContents.on("did-finish-load", () => {
      configPromise = new Promise((resolve) => {
        win.webContents.executeJavaScript(`window.__configFromElectron = ${JSON.stringify(getConfig())};`, () => {
          resolve();
        });
      });
    });
    
    // In your React app:
    async function init() {
      await configPromise;
      const config = window.__configFromElectron;
      // Use config here
    }
    
    init();
    

    This approach will ensure that the config object is set before any code that depends on it runs, but it does require modifying your React app to use an async initialization method.

    Login or Signup to reply.
  2. There are a few ways to do this, but my preferred way is to use the BrowserWindow‘s additionalArguments option.

    Note: this method can only send strings

    Main process

    const yourValue = 'any string value';
    const mainWindow = new BrowserWindow({
      ...opts
      webPreferences: {
        additionalArguments: ['--yourKey=${yourValue}']
      }
    })
    

    Preload script

    
    const {argv} = require('yargs-parser');
    
    const yourPassedValue = argv.yourKey; // 'any string value'
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search