yhirose / react-typescript-electron-sample-with-create-react-app-and-electron-builder

React-TypeScript-Electron sample with Create React App and Electron Builder
447 stars 96 forks source link

How to use ipcRenderer and ipcMain? #28

Open mrfambo opened 3 years ago

mrfambo commented 3 years ago

Can we use ipcRenderer in React App?

yhirose commented 3 years ago

Sorry that the question is out of scope of this library...

TeeGree commented 2 years ago

For anyone looking to use ipcRenderer with an Electron app built with TypeScript and React (following the directions in this repository's readme), I wanted to detail the steps I used.

Create a preload.ts file in the electron/ folder containing the following:

import { contextBridge, ipcRenderer } from 'electron';

contextBridge.exposeInMainWorld('electron', {
    doThing: () => ipcRenderer.invoke('do-thing')
});

Include that preload file in your main.ts file and properly handle the ipc invocation.

import { app, BrowserWindow, ipcMain } from 'electron';

...

win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
        nodeIntegration: true,
        // Include the .js file, since it will be compiled from .ts by the time it is used by electron
        preload: __dirname + '/preload.js'
    }
});

...

ipcMain.handle('do-thing', (event, message) => {
    console.log('Hello, World!');
});

With these steps, the window.electron.doThing() function is now available for use in your React components, but TypeScript won't recognize it. To get the typing working properly, you can add a .d.ts file with the definition in the src/ folder.`

Here is a sample renderer.d.ts:

export interface IElectronAPI {
    doThing: () => void,
}

declare global {
    interface Window {
        electron: IElectronAPI
    }
}

Finally, you can use the doThing() function in your React component. App.tsx:

import logo from './logo.svg';
import './App.css';
import Button from '@mui/material/Button';

function App() {
  const doThing = () => {
    window.electron.doThing();
  }

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <Button onClick={doThing}>Click me</Button>
      </header>
    </div>
  );
}

export default App;

I hope this helps! I spent a lot of time off and on this past couple months trying to find a way to actually combine TypeScript, React, Electron, and Material UI and still be able to access the file system, so I am hoping to save time for others looking to do the same. This library had most of the moving pieces close to correct, but I had to do some digging to figure out the IPC stuff.

yhirose commented 2 years ago

@TeeGree, thanks for the helpful information. I'll reopen this issue, so that others can benefit from your comment. :)

codestitch commented 2 years ago

On my end, I didn't use preload.js to create exposedApi, instead I used the IpcRenderer directly in ReactApp.

import { IpcRenderer } from "electron";

export const ipcRenderer: IpcRenderer = window.require("electron").ipcRenderer;

then I can immediately use the ipc's .send() and .on() function in any of my react pages.

import { ipcRenderer } from "../ipc";

export default function Shell() {
  useEffect(() => {
    ipcRenderer.on("main-menu:open", (_, data) => {
      console.log(data)
    });

    return () => {
      ipcRenderer.removeAllListeners("metadata:open");
    };
  }, []);

  const emitToMain= (item: string) => {
    ipcRenderer.send("notify", item);
  };

  return (<div>...</div>)
}