はじめに
Electron+React+redux+redux-sagaを使用した構成でアプリケーションを開発する際に、Electronのプロセス間通信の制御にredux-sagaのchannelを導入してみたので、まとめてみました。
Electronやredux、redux-sagaの説明についてはここでは割愛します。
使用したバージョン
Electron: 3.0.5
redux: 4.0.1
redux-saga: 0.16.2
Electronのプロセス間通信
ElectronではMainプロセスとRendererプロセスという2種類のプロセスが動いており、これらのプロセス間のやり取りは、それ用のオブジェクト(ipcMain、ipcRenderer)を用いて行います。詳しくは公式のドキュメントを参照。
今回はRendererプロセス内で、Mainプロセスからのメッセージを受け取る部分にredux-sagaのchannelを利用しました。
サンプルコード
以下がサンプルのコードになります。
まずはRendererプロセスからMainプロセスへメッセージを送る部分。これは公式のdocを参考にしています。
// In renderer process (web page).
const { ipcRenderer } = require("electron")
ipcRenderer.send("asynchronous-message", "ping")
続いて、MainプロセスでRendererプロセスからのメッセージを受け取る部分です。
// In main process.
const { ipcMain } = require("electron")
ipcMain.on("asynchronous-message", (event, arg) => {
console.log(arg) // prints "ping"
event.reply("asynchronous-reply", "pong")
})
最後に、RendererプロセスでMainプロセスからのメッセージを受け取る部分です。ここでredux-sagaのchannelを使用しています。
import { put, takeEvery } from "redux-saga/effects"
import { eventChannel } from "redux-saga"
const { ipcRenderer } = require("electron")
function* watchIpcChannelStart() {
// Mainプロセスからのメッセージを受け取るための eventChannel を作成
const channel = eventChannel((emit) => {
ipcRenderer.on("asynchronous-reply", () => {
emit(receiveAsynchronousEvent()) // dispatchしたいactionをemitの引数に渡す。
})
return () => { }
})
// 作成したchannelをtakeしてイベントを待ち受ける。
yield takeEvery(channel, handleIpcChannelStart)
}
function* handleIpcChannelStart(action) {
// actionをdispatchする
yield put(action)
}
export default function* rootSaga() {
yield fork(watchIpcChannelStart);
}
まとめ
以上、electronのプロセス間通信にredux-sagaを導入する方法でした。
実際に導入してみて感じた利点は、プロセス間通信もreduxのデータフロー(イベントが発火したらactionをdispatch→stateを更新→再レンダリング)に則ることができてわかりやすくなったことでしょうか。また、メインプロセスからのメッセージ受信に関わるコードを1箇所に集約できたのもよかったです。