从 React 组件创建的 Electron 中的 `preload.js` 中删除事件监听器
Remove event listener from `preload.js` in Electron created by React component
我正在使用 Electron 13 和 React 17。我已将 nodeIntegration
设置为 false,将 contextIsolation
设置为 true,因此我正在使用 preload.js
文件来公开 API 在主进程和渲染进程之间进行通信。
我必须使用这个 API 在 React 组件中收听 IPC 消息。但是,每次挂载(或重新渲染)我的组件时,Electron 都会创建一个新的 IPC 事件侦听器,从而导致内存泄漏。
preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
send: (channel, data) => {
// Whitelist channels
let validChannels = ['toMain'];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel, func) => {
let validChannels = ['fromMain'];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
}
});
ReactComponent.js
import { useEffect, useRef } from 'react';
const ReactComponent = () => {
const _isMounted = useRef(true);
const exampleFunction = () => {
window.api.send('toMain');
};
window.api.receive('fromMain', function (data) {
if (_isMounted.current) {
// Process `data`...
}
});
useEffect(() => {
exampleFunction();
// Another IPC call
window.api.send('toMain', ['example']);
window.api.receive('fromMain', function (data) {
// Process `data`...
});
return () => {
_isMounted.current = false;
// Somehow I should remove the IPC event listeners here,
// but I don't know how, since (I think) they are created
// in the `preload.js` file...
};
}, []);
return (
// JSX
);
};
export default ReactComponent;
如何注销通过 window.api.receive()
创建的事件侦听器?
This issue on GitHub 正好解决了这些问题。简而言之,在 preload.js
中创建的事件侦听器被分配给一个变量,因此脚本可以 return 回调以删除事件侦听器。
这是一个如何执行的例子:
preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
// ...
receive: (channel, func) => {
let validChannels = ['fromMain'];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
const subscription = (event, ...args) => func(...args);
ipcRenderer.on(channel, subscription);
return () => {
ipcRenderer.removeListener(channel, subscription);
};
}
},
});
ReactComponent.js
import { useEffect } from 'react';
const ReactComponent = () => {
const onEvent = (data) => {
// Process `data`...
};
useEffect(() => {
const removeEventListener = window.api.receive('fromMain', (data) => onEvent(data));
// ...
return () => {
removeEventListener();
};
}, []);
return (
// JSX
);
};
export default ReactComponent;
我正在使用 Electron 13 和 React 17。我已将 nodeIntegration
设置为 false,将 contextIsolation
设置为 true,因此我正在使用 preload.js
文件来公开 API 在主进程和渲染进程之间进行通信。
我必须使用这个 API 在 React 组件中收听 IPC 消息。但是,每次挂载(或重新渲染)我的组件时,Electron 都会创建一个新的 IPC 事件侦听器,从而导致内存泄漏。
preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
send: (channel, data) => {
// Whitelist channels
let validChannels = ['toMain'];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel, func) => {
let validChannels = ['fromMain'];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
}
});
ReactComponent.js
import { useEffect, useRef } from 'react';
const ReactComponent = () => {
const _isMounted = useRef(true);
const exampleFunction = () => {
window.api.send('toMain');
};
window.api.receive('fromMain', function (data) {
if (_isMounted.current) {
// Process `data`...
}
});
useEffect(() => {
exampleFunction();
// Another IPC call
window.api.send('toMain', ['example']);
window.api.receive('fromMain', function (data) {
// Process `data`...
});
return () => {
_isMounted.current = false;
// Somehow I should remove the IPC event listeners here,
// but I don't know how, since (I think) they are created
// in the `preload.js` file...
};
}, []);
return (
// JSX
);
};
export default ReactComponent;
如何注销通过 window.api.receive()
创建的事件侦听器?
This issue on GitHub 正好解决了这些问题。简而言之,在 preload.js
中创建的事件侦听器被分配给一个变量,因此脚本可以 return 回调以删除事件侦听器。
这是一个如何执行的例子:
preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
// ...
receive: (channel, func) => {
let validChannels = ['fromMain'];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
const subscription = (event, ...args) => func(...args);
ipcRenderer.on(channel, subscription);
return () => {
ipcRenderer.removeListener(channel, subscription);
};
}
},
});
ReactComponent.js
import { useEffect } from 'react';
const ReactComponent = () => {
const onEvent = (data) => {
// Process `data`...
};
useEffect(() => {
const removeEventListener = window.api.receive('fromMain', (data) => onEvent(data));
// ...
return () => {
removeEventListener();
};
}, []);
return (
// JSX
);
};
export default ReactComponent;