简体   繁体   中英

Electron with React- trying to import ipcRenderer - preload.js is not being called

I'm trying to send data fron the page to electron using ipc When I run the elctron app, the window.ipcRenderer is just undifined(inside the electron app)

this is the code

main.js:

const {app, BrowserWindow, ipcMain} = require('electron')
const path = require('path')

const mainWindow = new BrowserWindow({
    width: 1080,
    height: 920,
    fullscreenable: false,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

.

ipcMain.on("play-vlc-magnet", (event, magnet) => {
  //run_script("webtorrent --vlc " + magnet)
  console.log("recevied:", magnet)
})

preload.js(the console.log won't be in the console):

console.log("running preload")
window.ipcRenderer = require('electron').ipcRenderer;

line which is being called inside a react compon.net:

  window.ipcRenderer.send("play-vlc-magnet", r.data)

element inside React(which writes "ipc doesn't exist")

 <p>{(typeof window.ipcRenderer !== "undefined") ? "ipc renderer exists" : "ipc doesn't exist"</p>

Understanding and implementing a working preload.js script can be hard.

In the past I found it best to read and re-read the below points until you understand the difference between the main and render processes, what and why a preload.js script is used and how to implement and interact with a working preload.js script. After that, the sky is the limit.

It is common practice (as shown in the Electron documentation) to implement concrete functions within your preload.js script. Whilst there is nothing wrong with this approach I believe it can be easily confusing when trying to initially understand and use this approach.

Instead, my personal approach is to use only one preload.js script and code it for the transport of data via the use of whitelisted channel names only. This allows me to:

  1. Use only one preload.js script for all windows I create.
  2. Define in one place a master list of all whitelisted channel names.
  3. Have a small preload.js script file size, void of concrete implementations.
  4. Separate concerns and place the implementation of concrete function back into their proper (main and render process) domains.

In the code snippets below, I have show both the 'common' method programmers are shown in the Electron documentation and the alternative method I commonly use. You can decide which best suits your understanding and coding style.


Let's start with a working preload.js script, which is the part preventing you from communicating between your render process and the main process.

preload.js (main process)

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

contextBridge.exposeInMainWorld(
    'electronAPI', {
        playVlcMagnet: (data) => ipcRenderer.send('play-vlc-magnet', data)
    }
)

As an alternative to the above 'common' preload.js script, below is my personal preference.

preload-alt.js (main process)

// 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': [
            'play-vlc-magnet' // Channel name
        ],
        // 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)) {
                // 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);
            }
        }
    }
);

And how to use it.

/**
 * Render --> Main
 * ---------------
 * Render:  window.ipcRender.send('channel', data); // Data is optional.
 * Main:    ipcMain.on('channel', (event, data) => { methodName(data); })
 *
 * Main --> Render
 * ---------------
 * Main:    windowName.webContents.send('channel', data); // Data is optional.
 * Render:  window.ipcRender.receive('channel', (data) => { methodName(data); });
 *
 * Render --> Main (Value) --> Render
 * ----------------------------------
 * Render:  window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
 * Main:    ipcMain.handle('channel', (event, data) => { return someMethod(data); });
 *
 * Render --> Main (Promise) --> Render
 * ------------------------------------
 * Render:  window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
 * Main:    ipcMain.handle('channel', async (event, data) => {
 *              return await promiseName(data)
 *                  .then(() => { return result; })
 *          });
 */

Next, is your main.js file which you appear to have implemented correctly.

main.js (main process)

const app = require('electron').app;
const browserWindow = require('electron').BrowserWindow;
const ipcMain = require('electron').ipcMain;

const path = require('path');

let window;

function createWindow() {
    const window = new browserWindow({
        width: 1080,
        height: 920,
        fullscreenable: false,
        show: false,
        webPreferences: {
            nodeIntegration: false, // Default is false
            contextIsolation: true, // Default is true
            preload: path.join(__dirname, 'preload.js')  // Use of preload.js
            // preload: path.join(__dirname, 'preload-alt.js')  // Use of preload-alt.js
        }
    });

    window.loadFile('index.html')
        .then(() => { window.show(); });

    return window;
}

app.on('ready', () => {
    window = createWindow();
});

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate', () => {
    if (browserWindow.getAllWindows().length === 0) {
        createWindow();
    }
});

// ---

ipcMain.on("play-vlc-magnet", (event, magnet) => {
    // run_script("webtorrent --vlc " + magnet)
    console.log("received: ", magnet)
})

Lastly, your index.html file, which I do not have the code for, but you will get the idea.

index.html (render process)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Electron Test</title>
    </head>

    <body>
        <input type="button" id="button" value="Play VLC Magnet">
    </body>

    <script>
        let data = 'This is my data';

        document.getElementById('button').addEventListener('click', () => {
            window.electronAPI.playVlcMagnet(data); // Use of preload.js
            // window.ipcRender.send('play-vlc-magnet', data); // Use of preload-alt.js
        })
    </script>
</html>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM