[英]NodeJS events triggering multiple times in electron-react app
I have a package (Let's say PACKAGE_A
) written to do some tasks.我有一个 package (比方说
PACKAGE_A
)写来做一些任务。 Then it is required by PACKAGE_B
.然后
PACKAGE_B
需要它。 PACKAGE_A
is a node script for some automation work. PACKAGE_A
是一些自动化工作的节点脚本。 It has this Notifier module to create and export an EventEmitter.它有这个 Notifier 模块来创建和导出 EventEmitter。 (The Whole project is a Monorepo)
(整个项目是一个 Monorepo)
const EventEmitter = require('events');
let myNotifier = new EventEmitter();
module.exports = myNotifier;
So in some functions in PACKAGE_A
it emits event by requiring myNotifier
, and also in the index.js
of PACKAGE_A
, I export functions (API exposed to the other packages) and the myNotifier
by requiring it again.因此,在
PACKAGE_A
中的某些函数中,它通过要求myNotifier
发出事件,并且在PACKAGE_A
的index.js
中,我通过再次要求它来导出函数(暴露给其他包的 API)和myNotifier
。
const myNotifier = require('./myNotifier);
const func1 = () => {
// some function
return something;
}
module.exports = {func1, myNotifier}
Then I import the PACKAGE_A
in PACKAGE_B
and use the API functions exposed with the notifier.然后我在
PACKAGE_A
中导入PACKAGE_B
并使用通知程序公开的 API 函数。 PACKAGE_B
is an electron app with a React UI. PACKAGE_B
是一个带有 React UI 的 electron 应用程序。
Below is how the program works.以下是该程序的工作原理。
I have a console output window in the electron app (React UI, UI_A ).我在 electron 应用程序(React UI, UI_A )中有一个控制台 output window。 <= (keep this in mind)
<=(记住这一点)
When I click a button in UI_A it fires a redux action ( button_action
).当我单击UI_A中的按钮时,它会触发redux 操作(
button_action
)。 Inside the action, a notification is sent to an event which is listened in the electron code using ipcRenderer.在动作内部,使用 ipcRenderer 向在 electron 代码中侦听的事件发送通知。
ipcRenderer.send('button-clicked', data); // <= this is not the full code of the action. It's bellow.
Then in the electron code (index.js), I require another file ( UI_A_COM.js
which houses the code related to UI_A in electron side).然后在 electron 代码(index.js)中,我需要另一个文件(
UI_A_COM.js
包含与electron端的 UI_A 相关的代码)。 The reason is code separation.原因是代码分离。 Here's part of the code in
index.js
related to the electron.这是
index.js
中与 electron 相关的部分代码。
const ui_a_com = require('./electron/UI_A_COM');
const createWindow = () => {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
},
resizable: false,
});
mainWindow.loadURL('http://localhost:3000');
const mainMenu = Menu.buildFromTemplate(menuTemplate);
ui_a_com (mainWindow);
};
Alright.好吧。 Then in
UI_A_COM.js
, I listen to that triggered event button-clicked
.然后在
UI_A_COM.js
中,我听那个触发的事件button-clicked
。
ipcMain.on('button-clicked', someFunction); ipcMain.on('按钮点击', someFunction);
which runs the code from PACKAGE_A
and return a result.它从
PACKAGE_A
运行代码并返回结果。 So now when PACKAGE_A
runs, it emits some events using myNotifier
.所以现在当
PACKAGE_A
运行时,它会使用myNotifier
发出一些事件。 I listen to them in the same file ( UI_A_COM.js
), and when those events are captured, I again send some events to React UI, which is subscribed when button_action
fired.我在同一个文件 (
UI_A_COM.js
) 中收听它们,当这些事件被捕获时,我再次将一些事件发送到 React UI,当button_action
触发时订阅。
myNotifier.on('pac_a_event_a', msg => {
mainWindow.webContents.send('ui_event_a', msg); // code in `UI_A_COM.js`
});
Here's the full code for the action.这是该操作的完整代码。 (Did not provide earlier because you'll get confused)
(之前没有提供,因为你会感到困惑)
export const buttonAction = runs => {
return dispatch => {
ipcRenderer.send('button-clicked', data);
ipcRenderer.on('ui_event_a', (event, msg) => {
dispatch({ type: SOME_TYPE, payload: { type: msg } });
});
};
};
This will show the msg in the UI_A console.这将在UI_A控制台中显示消息。
So this is the task I'm doing.这就是我正在做的任务。 The problem is when I click the button;
问题是当我单击按钮时; it works perfectly for the first time.
它第一次完美运行。 But when I click the button on the second time, it received two messages.
但是当我第二次单击该按钮时,它收到了两条消息。 Then when I click the button again, three messages and it keeps growing.
然后,当我再次单击该按钮时,会出现三条消息,并且它一直在增长。 (but the functions in the PACKAGE_A only executes one time per button press).
(但 PACKAGE_A 中的函数每次按下按钮仅执行一次)。
Let's say the message from PACKAGE_A
emitted is 'Hello there' per execution.假设每次执行时发出的
PACKAGE_A
发出的消息是“Hello there”。
When I press the button 1st time a perfect result => Hello there, When I click the button again => Hello there Hello there, When I click it again => Hello there Hello there Hello there.当我第一次按下按钮时,一个完美的结果 => 你好,当我再次点击按钮 => 你好,你好,当我再次点击它时 => 你好,你好,你好。
It's kept so on.一直这样。 I think my implementation of EventEmitter has some flows.
我认为我的 EventEmitter 实现有一些流程。 So why it's happening like this?
那么为什么会这样呢? Is it EventEmitter or something else?
是 EventEmitter 还是别的什么? What am I doing wrong here?
我在这里做错了什么?
I think you should return a function that call ipcRenderer.removeAllListeners()
in your component's useEffect()
.我认为您应该在组件的
useEffect()
返回一个调用ipcRenderer.removeAllListeners()
的函数。 Because every time you click your custom button, the ipcRenderer.on(channel, listener)
is called, so you set a listener to that channel agin and agin...因为每次单击自定义按钮时,
ipcRenderer.on(channel, listener)
,因此您将侦听器设置为该频道 agin 和 agin...
Example:例子:
useEffect(() => {
electron.ipcRenderer.on('myChannel', (event, arg) => {
dispatch({ type: arg });
});
return () => {
electron.ipcRenderer.removeAllListeners('myChannel');
};
});
By default the electron-react boilerplate doesnt define the ipcRenderer.removeAllListeners
method.默认情况下,电子反应样板没有定义
ipcRenderer.removeAllListeners
方法。 So you have to first go to the main/preloads.ts
file and add them:所以你必须首先 go 到
main/preloads.ts
文件并添加它们:
removeListener(channel: string, func: (...args: unknown[]) => void) {
ipcRenderer.removeListener(channel, (_event, ...args) => func(...args));
},
removeAllListeners(channel: string) {
ipcRenderer.removeAllListeners(channel);
},
Then go to the renderer/preload.ts
declaration file and add the declarations too:然后 go 到
renderer/preload.ts
声明文件并添加声明:
removeListener(
channel: string,
func: (...args: unknown[]) => void
): void;
removeAllListeners(channel: string): void;
After that make sure to clean all listeners in the cleanup function of your useEffects each time you listen to an event fired.之后,确保在每次收听触发的事件时清除 useEffects 的清理 function 中的所有侦听器。 This will prevent multiple firing.
这将防止多次射击。
useEffect(() => {
window.electron.ipcRenderer.on('myChannel', (event, arg) => {
// do stuffs
});
return () => {
window.electron.ipcRenderer.removeAllListeners('myChannel');
};
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.