👓
Electron上のReactからFile Dialogを出す
使うAPIはとても簡単なのですが、核心を突く記事が見つからない気がするので…
TL; DR
ipcMain
と ipcRenderer
を使ってメインプロセスに showOpenDialog
させる
例
古い記事に当たると、レンダラからメインを直接参照するっぽい remote
というものを使っていたりするのですが、これは deprecated なので使わないようにしましょう
使い方は Socket.io を使ったことのある方には直感的だと思います
main.ts
let mainWindow
const addIpcListener = () => {
ipcMain.on('open-file-dialog', async (event, payload?: OpenDialogOptions) => {
if (!mainWindow) {
event.reply('open-file-dialog')
return
}
const value = await dialog.showOpenDialog(mainWindow, {
...payload
})
event.reply('open-file-dialog', value)
})
}
const createWindow = async () => {
mainWindow = new BrowserWindow({ ... })
addIpcListener()
}
renderer.ts
ipcRenderer.on('open-file-dialog', (_event, result) => {
if (result.canceled) {
return
}
const filePaths = result.filePaths
// 処理
})
ipcRenderer.send('open-file-dialog', {
title: 'Select a file',
filters: [{
name: 'CSV file',
extensions: ['csv']
}]
})
Channelと呼ばれる文字列 (ここでは 'open-file-dialog'
) の一致したリスナが呼ばれます
レンダラから send
するとメインの on
に渡したコールバックが呼ばれて、その中で reply
するとさらにメインからレンダラにオブジェクトを返すことができます
通信内容はたぶんJSONかなにかなので、シリアライズ不可能なオブジェクト (循環参照を持つものなど) は怒られます
Tips
レンダラ側ではファイルが返ってくるまで待ちたかったりするので、こういう関数を書くと捗ると思います
ipc.ts
const ipc = <T, U>(channel: Channel, payload?: T): Promise<U> => new Promise((resolve) => {
ipcRenderer.once(channel, (_event, args: U) => {
resolve(args)
})
ipcRenderer.send(channel, payload)
})
once
は1度受け取るとremoveされるリスナ (参考: ipcRenderer)
Discussion