GithubHelp home page GithubHelp logo

sindresorhus / electron-better-ipc Goto Github PK

View Code? Open in Web Editor NEW
700.0 8.0 60.0 41 KB

Simplified IPC communication for Electron apps

License: MIT License

JavaScript 80.80% HTML 0.87% TypeScript 18.34%
electron-module electron ipc-message ipc nodejs npm-package

electron-better-ipc's Introduction

electron-better-ipc

Simplified IPC communication for Electron apps

The biggest benefit of this module over the built-in IPC is that it enables you to send a message and get the response back in the same call. This would usually require multiple IPC subscriptions.

You can use this module directly in both the main and renderer process.

Install

npm install electron-better-ipc

Requires Electron 10 or later.

Usage

Using the built-in IPC

Here, as an example, we use the built-in IPC to get an emoji by name in the renderer process from the main process. Notice how it requires coordinating multiple IPC subscriptions.

Main
const {ipcMain: ipc} = require('electron');

ipc.on('get-emoji', async (event, emojiName) => {
	const emoji = await getEmoji(emojiName);
	event.sender.send('get-emoji-response', emoji);
});
Renderer
const {ipcRenderer: ipc} = require('electron');

ipc.on('get-emoji-response', (event, emoji) => {
	console.log(emoji);
	//=> 'πŸ¦„'
});

ipc.send('get-emoji', 'unicorn');

Using this module

As you can see below, this module makes it much simpler to handle the communication. You no longer need multiple IPC subscriptions and you can just await the response in the same call.

Main
const {ipcMain: ipc} = require('electron-better-ipc');

ipc.answerRenderer('get-emoji', async emojiName => {
	const emoji = await getEmoji(emojiName);
	return emoji;
});
Renderer
const {ipcRenderer: ipc} = require('electron-better-ipc');

(async () => {
	const emoji = await ipc.callMain('get-emoji', 'unicorn');
	console.log(emoji);
	//=> 'πŸ¦„'
})();

Here we do the inverse of the above, we get an emoji by name in the main process from the renderer process:

Renderer
const {ipcRenderer: ipc} = require('electron-better-ipc');

ipc.answerMain('get-emoji', async emojiName => {
	const emoji = await getEmoji(emojiName);
	return emoji;
});
Main
const {ipcMain: ipc} = require('electron-better-ipc');

(async () => {
	const emoji = await ipc.callFocusedRenderer('get-emoji', 'unicorn');
	console.log(emoji);
	//=> 'πŸ¦„'
})();

API

The module exports ipcMain and ipcRenderer objects which enhance the built-in ipc module with some added methods, so you can use them as a replacement for electron.ipcMain/electron.ipcRenderer.

Main process

ipcMain.callRenderer(browserWindow, channel, data?)

Send a message to the given window.

In the renderer process, use ipcRenderer.answerMain to reply to this message.

Returns a Promise<unknown> with the reply from the renderer process.

browserWindow

Type: BrowserWindow

The window to send the message to.

channel

Type: string

The channel to send the message on.

data

Type: unknown

The data to send to the receiver.

ipcMain.callFocusedRenderer(channel, data?)

Send a message to the focused window, as determined by electron.BrowserWindow.getFocusedWindow.

In the renderer process, use ipcRenderer.answerMain to reply to this message.

Returns a Promise<unknown> with the reply from the renderer process.

channel

Type: string

The channel to send the message on.

data

Type: unknown

The data to send to the receiver.

ipcMain.answerRenderer(channel, callback)

This method listens for a message from ipcRenderer.callMain defined in a renderer process and replies back.

Returns a function, that when called, removes the listener.

channel

Type: string

The channel to send the message on.

callback(data?, browserWindow)

Type: Function | AsyncFunction

The return value is sent back to the ipcRenderer.callMain in the renderer process.

ipcMain.answerRenderer(browserWindow, channel, callback)

This method listens for a message from ipcRenderer.callMain defined in the given BrowserWindow's renderer process and replies back.

Returns a function, that when called, removes the listener.

browserWindow

Type: BrowserWindow

The window for which to expect the message.

channel

Type: string

The channel to send the message on.

callback(data?, browserWindow)

Type: Function | AsyncFunction

The return value is sent back to the ipcRenderer.callMain in the renderer process.

ipcMain.sendToRenderers(channel, data?)

Send a message to all renderer processes (windows).

channel

Type: string

The channel to send the message on.

data

Type: unknown

The data to send to the receiver.

Renderer process

ipcRenderer.callMain(channel, data?)

Send a message to the main process.

In the main process, use ipcMain.answerRenderer to reply to this message.

Returns a Promise<unknown> with the reply from the main process.

channel

Type: string

The channel to send the message on.

data

Type: unknown

The data to send to the receiver.

ipcRenderer.answerMain(channel, callback)

This method listens for a message from ipcMain.callRenderer defined in the main process and replies back.

Returns a function, that when called, removes the listener.

channel

Type: string

The channel to send the message on.

callback(data?)

Type: Function | AsyncFunction

The return value is sent back to the ipcMain.callRenderer in the main process.

Related

electron-better-ipc's People

Contributors

bendingbender avatar dusansimic avatar garrylachman avatar jackple avatar javierron avatar karaggeorge avatar lukechilds avatar npezza93 avatar richienb avatar sindresorhus avatar stefnotch avatar t13m avatar ulken avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

electron-better-ipc's Issues

Errors passed as empty objects

Issuehunt badges

In answerRenderer and answerMain errors get passed directly to electron ipc, which serializes Errors as plain objects (electron/electron#7956)

I made a patch that uses your serialize-errors library, but didn't create a pull request because I'm not sure my changes to the test fixtures are up to snuff.

patch: lselden@1f3d72e


IssueHunt Summary

jackple jackple has been rewarded.

Backers (Total: $90.00)

Submitted pull Requests


Tips


IssueHunt has been backed by the following sponsors. Become a sponsor

`sendToRenderers` acts differently than `callRenderer`

From documentation I expected that these two functions act the same. Only expected difference was callRenderer sending to specific window and sendToRenderers sending to BrowserWindow.getAllWindows().

After digging deeper I found that sendToRenderers does not use the same channel formatting as the other method, so it is impossible to handle incoming messages using ipcRenderer.answerMain.

I have two possible solutions:

  1. Exposing additional function in ipcRenderer to handle unchanged channel.
  2. Formatting channel in sendToRenderers for every window.

@sindresorhus which one would you prefer?

Can't resolve 'fs'

As soon as I use this in my webpack build (via angular cli) for the renderer I get the error above.

import { ipcRenderer } from 'electron-better-ipc';
console.log(ipcRenderer);

Leads to:

Module not found: Error: Can't resolve 'fs' in '/home/mememe/www/super-productivity/node_modules/electron'

Calling `ipc.callFocusedRenderer` when no windows are focused

The following error is thrown when no windows are focused and ipc.callFocusedRenderer is called:

TypeError: Cannot read property 'id' of null
    at [node_modules]/electron-better-ipc/source/main.js:10:98
    at new Promise (<anonymous>)
    at IpcMainImpl.ipc.callRenderer ([node_modules]/electron-better-ipc/source/main.js:9:54)
    at IpcMainImpl.ipc.callFocusedRenderer ([node_modules]/electron-better-ipc/source/main.js:46:44)

Took me a bit of time to debug, should probably help another person googling it. Would be good to handle this and throw a more descriptive error message.

cc #27 @ulken

Use `any` instead of `unknown`

I'm working on sindresorhus/caprine#848 and I've hit a wall when I wanted to receive data from answerRendered function. The problem is passed data is defined in answerRenderer with unknown type which can't later on be assigned to other values with any other type than unknown.

My best idea is to use type any instead of unknown. Other option is to use unknown type on every ipc handling in Caprine which doesn't sound very nice to me.

@BendingBender I'd love your thoughts on this too since you wrote up TypeScript definitions.

BetterIpcRenderer on method only gets called once

As the title says the on method of electron-better-ipcs ipcRenderer only gets called once and the original on get called every time a message is sent.

// Renderer
ipcRenderer.on("channel", console.log)

// Main
ipcMain.sendToRenderers("channel", "test")
ipcMain.sendToRenderers("channel", "test")

This example only logs once with the better ipc one and twice with the vanilla version

QUESTION: Any reason for mutating ipcRenderer / ipcMain?

  1. Any reason for mutating ipcRenderer / ipcMain? (I'm asking that as if it would be using "prototype extensions" of browser techs)
  2. Wouldn't be better to export { ipcRenderer, ipcMain } instead of ipc which changed by the environment? (It's true, it's a bit cleaner, but they're still have different shape which mean, more confusion and not allowing creating proper type definitions)

Overlapping calls seem to return the first available result

By inspection, it seems like the following scenario will not work:

  1. main calls answerRenderer for channel "makeUpper"
  2. renderer calls callMain for channel "makeUpper" and data "aaa"
  3. renderer calls callMain for channel "makeUpper" and data "bbb"
  4. Callback for "makeUpper" of "aaa" returns "AAA"
  5. Both renderer calls to callMain receive this data (they are using the same channel)
  6. Code thinks makeUpper of "bbb" returned "AAA"

When I implemented something similar (before this module was available), I needed a unique identifier to correlate request/response and avoid this problem: https://github.com/DavidAnson/Lenz/blob/master/ipc.js

Might something like that be necessary here?

Cant receive ipc msg after webpack hot reload

main.js
ipcMain.answerRenderer(IPCMsg.GetConfig, () => { return config.data })

renderer.js
async load () { let data = await ipcRenderer.callMain(IPCMsg.GetConfig) console.log(data) }

Everything is normal at startup, but after modifying some code to trigger a hot update, ipcRenderer can only send messages, can't receive messages.

In electron-better-ipc/source/renderer.js:6
const ipc = Object.create(ipcRenderer);
Modify to
const ipc = ipcRenderer;
Fixed this bug

I don't know why

Simplify calling the focused window

Issuehunt badges

Having to do this every time is slightly inconvenient:

const win = electron.BrowserWindow.getFocusedWindow();

(async () => {
	const emoji = await ipc.callRenderer(win, 'get-emoji', 'unicorn');
	console.log(emoji);
	//=> 'πŸ¦„'
})();

I'm thinking something like:

(async () => {
	const emoji = await ipc.callFocusedRenderer('get-emoji', 'unicorn');
	console.log(emoji);
	//=> 'πŸ¦„'
})();

Thoughts?


IssueHunt Summary

ulken ulken has been rewarded.

Backers (Total: $40.00)

Submitted pull Requests


Tips


IssueHunt has been backed by the following sponsors. Become a sponsor

ipcRenderer.callMain - subsequent calls return non-resolving Promises

When ipcRenderer.callMain gets called two or more times, the first Promise resolves normally, but subsequent calls return non-resolving Promises.
I'm using typescript and electron-webpack. I've thrown together a quick bug repro repo to ilustrate the problem.
repro
I did a bit of investigation around the method's source code and found out that in the second call, the callbacks (onData, onError) were not called. But, when I commented out these lines, the issue disappeared (at least for my test case)

ipc.callMain = (channel, data) => new Promise((resolve, reject) => {
	const {sendChannel, dataChannel, errorChannel} = util.getResponseChannels(channel);

	const cleanup = () => {
-		ipc.off(dataChannel, onData);
-		ipc.off(errorChannel, onError);
+		// ipc.off(dataChannel, onData);
+		// ipc.off(errorChannel, onError);
	};

	const onData = (event, result) => {
		cleanup();
		resolve(result);
	};

	const onError = (event, error) => {
		cleanup();
		reject(deserializeError(error));
	};

	ipc.once(dataChannel, onData);
	ipc.once(errorChannel, onError);

In my opinion the off calls are redundant anyways, because the listeners are attached using once, so they will be removed automatically after first event regardless (and that's what happens after my change, as far as I know). I feel like the double removal might be the issue here.

Issue when building outside renderer process

Hi

First of all, thank you so much for this very convenient IPC wrapper πŸ™πŸ»

I've just integrated it to my project and everything is working flawlessly... almost!

Unfortunately I'm having an issue when building my renderer project.

This line:

const ipc = Object.create(ipcRenderer);

Results in:
Object prototype may only be an Object or null: undefined

This happens because const {ipcRenderer} = electron; is resulting in undefined when running outside the renderer process - for example when building / prerendering ☺️

Could be the same issue causing #15

Issues with receiving data from callMain

Issuehunt badges

Hello, I've run into this issue over the past week that I've been unable to move beyond, and I would appreciate another pair of eyes.

In my renderer, I have 2 separate pieces of code that use callMain to get a response back from the main process. One of them returns data just fine, the other returns nothing at all, despite logging showing that the main listener receives it and returns successfully. I even used devtron to see that a message is getting sent to the renderer, but for some reason the listener is not working as expected.

I wonder if I am just fundamentally misinterpreting the library, or if there is something causing my second call to fail. Any thoughts would be appreciated. Main and rendering code is shown below:

renderer.js

// this function works fine
function* checkExistingUser() {
  console.log('checking existing user.');
  const resp = yield ipc.callMain('check-existing-user', 'TESTUSER');
  if (resp.error) {
    console.error('Received error from ipc main');
    return;
  }
  // other behavior
}

// this function doesn't return data from callMain
function* unlockUserCredentials(action) {
  let localUserData = yield select(getLocalData);
  // code makes it here, logging shows up
  try {
    const resp = yield ipc.callMain('unlock-user-credentials', credData);
    console.log(resp); // never shows up in logs
  } catch (error) {
    console.log(error); // never shows up in logs
  }
}

My main file:

// this function responds fine, and data is accessible on the client
ipc.answerRenderer('check-existing-user', async data => {
  // working returns
  return {};
});

// this function returns successfully, even with try catch
ipc.answerRenderer('unlock-user-credentials', async data => {
  return {
    message: 'sample data',
  };
});

am I doing something wrong by having multiple ipc answer renderers here? Any thoughts or ideas are appreciated. As a note, my renderer code is written for use with redux-saga.


IssueHunt Summary

garrylachman garrylachman has been rewarded.

Backers (Total: $60.00)

Submitted pull Requests


Tips


IssueHunt has been backed by the following sponsors. Become a sponsor

Better Typescript Support

It'd be lovely if this library came with excellent Typescript support, where one can define which "ipc methods" actually exist.
Like being able to say

type MainIpc = {
  "get-emoji"(name: string): string;
};

Then doing a bit to annotate the IPC with said types, like const ipc: SafeMainProcessIpc<MainIpc, RendererIpc> = ipcMain;

After that, one could have excellent autocomplete and Typescript errors if something doesn't add up.
image

HELP Problem communicating main -> renderer

Hello,
I'm new in the world of Java Script.
I use Electron-vue and I have some problems with this module.

I am able to send message from renderer to main and get a beautiful unicorn.
But when I have to do the reverse, I'm stuck.
I tried to put the main part in my main.js.
Then,

const win = electron.BrowserWindow.getFocusedWindow();

returns 'undefined'.

I also tried to put it in main/index.js at the end of my file (it's the file that create the window of the app).
And then it returns 'null'.

I have really no idea to make all things in order... Can someone help me please?

(if you understood nothing to what is the files I am talking about, here is my project: https://github.com/Irraky/test_mDNS)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.