Home > OS >  Electron contextBridge returns undefined
Electron contextBridge returns undefined

Time:05-05

I've got these 4 project files:

main.js
preload.js
renderer.js
index.html

Node: 17.4.0 Electron: 18.2.0

I'm attempting to open a text file on my filesystem, triggered by a click event from renderer.js - then load the text file's contents into a <div> tag in index.html.

main.js

const {app, BrowserWindow, ipcMain} = require("electron");
const path = require("path");
const fs = require("fs");

const createWindow = () => {
    // Create the browser window.
    const mainWindow = new BrowserWindow({
         webPreferences: {
            preload: path.join(__dirname, 'preload.js'),
            contextIsolation: true,
            nodeIntegration: false
         }
    });
    mainWindow.loadFile(path.join(__dirname, "index.html"));

    // Open the DevTools.
    mainWindow.webContents.openDevTools();
};

app.on("ready", () => {
    createWindow();

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


function openFile(){
    fs.readFile("logs.txt", "utf8", (err, data) => {
        if (err) {
            console.error(err);
            return "Error Loading Log File";
        }
        console.log(data);
        return data;
    });
}

ipcMain.handle("channel-load-file", openFile);

preload.js

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

contextBridge.exposeInMainWorld("electronAPI", {
    loadFile: () => ipcRenderer.invoke("channel-load-file")
});

renderer.js

const btn = document.querySelector("#btn");
btn.addEventListener("click", e => {
   let data = window.electronAPI.loadFile();
   document.getElementById("main-content").innerText = data;
});

I can definitely see the contents of the Log file inside console.log(data); in the main.js

But, the <div id="main-content"></div> gets populated with undefined.

I believe I'm missing some crucial step within either: preload.js or renderer.js

Anyone see where the chain of events is getting lost?

(I'm very open to any improvements to my flow)

CodePudding user response:

Inserting console.log()'s in the code below indicates that the handle content is executed before openFile has a chance to return a result.

main.js (main process)

function openFile() {
    fs.readFile("logs.txt", "utf-8", (err, data) => {
        if (err) {
            console.error(err);
            return "Error Loading Log File";
        }

        console.log('openFile: '   data); // Testing

        return data;
    });
}

ipcMain.handle('channel-load-file', () => {
    let result = openFile();

    console.log('handle: '   result); // Testing

    return result;
})

The console.log() results are...

handle: undefined
openFile: File content...

To fix this, let's change fs.readFile from a callback to a promise, so we can await for it in the handle.

As the handle is dealing with a promise, let's use the syntactic sugar async and await for easier implementation.

main.js (main process)

function openFile() {
    return new Promise((resolve, reject) => {
        fs.readFile("logs.txt", "utf-8", (error, data) => {
            if (error) {
                console.log('reject: '   error); // Testing
                reject(error);
            } else {
                console.log('resolve: '   data); // Testing
                resolve(data)
            }
        });
    });
}

ipcMain.handle('channel-load-file', async (event, message) => {
    return await openFile()
        .then((data) => {
            console.log('handle: '   data); // Testing
            return data;
        })
        .catch((error) => {
            console.log('handle error: '   error); // Testing
            return 'Error Loading Log File';
        })
});

Lastly, let's modify the way we retrieve the data in the index.html file.

PS: Let's also add .toString() to the returned data (just to be sure).

index.html (render process)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Electron Test</title>
        <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
    </head>

    <body>
        <div id="main-content"></div>

        <input type="button" id="button" value="Load File">
    </body>

    <script>
        document.getElementById('button').addEventListener('click', () => {
            window.electronAPI.loadFile()
                .then((data) => {
                    console.log(data); // Testing
                    document.getElementById("main-content").innerText = data.toString();
                });
        })
    </script>
</html>
  • Related