Reputation: 6060
I have a working websocket server. I use a websocket as client in web browser/react before, but I'm unable to use Websocket inside electron app since WebSocket depends on browser's compatibility and for some reason, this feature is unavailable in Electron.
I use nextron (nextjs/react + electron) boilerplate.
yarn create nextron-app MY_APP --example with-typescript-material-ui
import React from 'react';
import Head from 'next/head';
import { ThemeProvider } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import { theme } from '../lib/theme';
import type { AppProps } from 'next/app';
export default function (props: AppProps) {
const { Component, pageProps } = props;
// where to put ws here ? this placement generates an error
const ws = new WebSocket("ws://192.168.100.8:8081/")
console.log("file: _app.tsx:11 ~ ws", ws)
React.useEffect(() => {
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<React.Fragment>
<Head>
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
</Head>
<ThemeProvider theme={theme}>
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</React.Fragment>
);
}
Upvotes: 3
Views: 1560
Reputation: 6060
Apparently it can't be done. Electron blocks any attempt to use websocket inside the client (React). The only solution so far I use is to convert websocket messages and events into ipcRenderer
(https://www.electronjs.org/docs/latest/api/ipc-renderer)
the process is fairly simple:
React receives an event and calls the IPCRenderer on the client, The IPCRenderer receive an event in the renderer (node.js) and call upon the ws
or the websocket in the node.js
Sending:
React -> IPC -> Node.js -> websocket -> Websocket Server (main server)
Receiving:
Websocket Server (main server) -> websocket -> Node.js -> IPC -> React
I use the ws
module fro node.js from here
I hope this can help someone or myself in the future
If you want to send/broadcast an event to websocket
// in the client side usually the root component, or in my case _app.tsx:
import electron from 'electron'
const ipcRenderer = electron.ipcRenderer
class _app extends React.Component<any, any> {
constructor(props: any) {...}
componentDidMount() {...}
// this will send a message for a button click
handleClick(msg: string) {
// reply will be true if it succeed
let reply = ipcRenderer.sendSync('some-event', msg)
}
}
later in the main window app.js:
import { app, ipcMain } from 'electron'
import WebSocket from 'ws'
// connect with ws
let ws = new WebSocket(`ws://${YOUR_WS_SERVER_IP}:${YOUR_WS_PORT}/`)
// find the electron main window, mine's in background.ts
const mainWindow = createWindow('main', {
width: 1366,
height: 768,
minWidth: 1366,
minHeight: 768
})
// .... some electron code here ....
// when ipcMain receive an event named 'some-event'
ipcMain.on('some-event', (event, msg) => {
ws.send(msg) // send message using websocket here
event.returnValue = true // give a return value true
})
If you want to handle an event received from websocket
import { app, ipcMain } from 'electron'
import WebSocket from 'ws'
// connect with ws
let ws = new WebSocket(`ws://${YOUR_WS_SERVER_IP}:${YOUR_WS_PORT}/`)
// find the electron main window, mine's in background.ts
const mainWindow = createWindow('main', {
width: 1366,
height: 768,
minWidth: 1366,
minHeight: 768
})
ws.on("message", (message: any) => {
var str = message.toString()
console.log("Message received: ", str)
mainWindow.webContents.send('some-event', str)
})
on the react component (App.tsx) :
// in the client side usually the root component, or in my case _app.tsx:
import electron from 'electron'
const ipcRenderer = electron.ipcRenderer
class _app extends React.Component<any, any> {
constructor(props: any) {
super(props)
// I put this since I use class based component.
// a functional ones won't need this
this.handleIpc = this.handleIpc.bind(this)
}
componentDidMount() {
this.handleIpc() // to make sure the ipcRenderer manipulate the component state AFTER the whole component was loaded first
}
handleIpc() {
var self = this
ipcRenderer.on("some-event", function (e, data) {
console.log("Message received: ", data)
})
}
}
Upvotes: 2