I am working with WebSocket in reactjs, but WebSocket.send is not executing on the onClick event.
here I create WebSocket
const ws = new WebSocket("ws://192.168.18.112:8000/ws/notification/");
connect to WebSocket
ws.onopen = (e) => {
console.log("connect");
};
here is my onClick function
const sendNotification = (e) => {
console.log("click");
if (ws.readyState === WebSocket.OPEN && message !== "") {
ws.send("message");
console.log("Sending message"); // this console is execute
} else if (ws.readyState === WebSocket.CONNECTING) {
ws.addEventListener("open", () => sendNotification());
console.log("Connecting state");
console.log("Not Send");
} else {
console.log("nothing happen");
}
};
so here is my problem I am unable to find bug or problem in my code.
try this W3CWebSocket. Also i don't think there si a "/" after notification after ws/notification double check if your end point correct or not.
mport React, { Component } from 'react';
import { w3cwebsocket as W3CWebSocket } from "websocket";
const client = new W3CWebSocket('ws://192.168.18.112:8000/ws/notification/');
class App extends Component {
componentWillMount() {
client.onopen = () => {
console.log('WebSocket Client Connected');
};
client.onmessage = (message) => {
console.log(message);
};
}
render() {
return (
<div>
Practical Intro To WebSockets.
</div>
);
}
}
export default App;
Related
So I'm primarily a C++ backend developer but I'm learning React on the side and I have this really simple file to use websockets.
import React, { useState } from "react";
var serverMessage = "";
var webSocketReady = false;
function connect() {
webSocket = new WebSocket("ws://127.0.0.1:3000/ws")
webSocket.onopen = (event) => {
webSocketReady = true;
};
webSocket.onmessage = function (event) {
serverMessage = JSON.parse(event.data);
};
webSocket.onclose = function (event)
{
webSocketReady = false;
setTimeout(function() {
connect();
}, 1000);
};
webSocket.onerror = function (err)
{
console.log('Socket encountered error: ', err.message, 'Closing socket')
webSocket.close();
};
}
connect();
export default function MyTestComponent({
...props
}) {
const [varThatNeedHooks, setVar] = useState({});
if (!webSocketReady)
{
return (<h1>Could not connect to server retrying ...</h1>);
}
else if (serverMessage == "")
{
return (<h1>Waiting for message from server ...</h1>);
}
else
{
// do stuff with varThatNeedHooks
}
}
I'm looking at the react documentation and I realized this way that I'm connecting the websocket to MyTestComponent has got to be wrong. But I'm not sure what is the "React" way of hooking up the events from the websocket to change what gets rendered. Anyone have any good documents or examples I can follow?
Typically you'd make serverMessage and webSocketReady part of the React state so that when they are updated it triggers a component rerender. Use an useEffect hook to manage the socket instance.
Example:
import React, { useEffect, useState, useRef } from "react";
export default function MyTestComponent({ ...props }) {
const [varThatNeedHooks, setVar] = useState({});
const [serverMessage, setServerMessage] = useState("");
const [webSocketReady, setWebSocketReady] = useState(false);
const [webSocket, setWebSocket] = useState(new WebSocket("ws://127.0.0.1:3000/ws"));
useEffect(() => {
webSocket.onopen = (event) => {
setWebSocketReady(true);
};
webSocket.onmessage = function (event) {
setServerMessage(JSON.parse(event.data));
};
webSocket.onclose = function (event) {
setWebSocketReady(false);
setTimeout(() => {
setWebSocket(new WebSocket("ws://127.0.0.1:3000/ws"));
}, 1000);
};
webSocket.onerror = function (err) {
console.log('Socket encountered error: ', err.message, 'Closing socket');
setWebSocketReady(false);
webSocket.close();
};
return () => {
webSocket.close();
};
}, [webSocket]);
if (!webSocketReady) {
return <h1>Could not connect to server retrying ...</h1>;
} else if (serverMessage == "") {
return <h1>Waiting for message from server ...</h1>;
} else {
// do stuff with varThatNeedHooks
}
}
I set a timeout function to wait and trigger reconnection after the connection is broken.
My Angular version is 11.
The following is my WebsocketService code:
import { Injectable } from '#angular/core'
#Injectable()
export class WebSocketService {
constructor() {
this.init();
}
init() {
var ws = new WebSocket('ws://localhost:8080');
ws.onopen = (event) => {
console.log('WebSocket connected');
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data);
}
ws.onclose = (event) => {
console.log('Close WebSocket connection');
setTimeout(this.init, 1000);
}
ws.onerror = (event) => {
console.log(`WebSocket connection error: ${event}`);
ws.close();
}
}
}
My problem is that setTimeout function always be triggered twice.
Then it will not work anymore.
Have any suggestion?
Thanks!
I'm working on app which send message via websockets (managed by django channels) and in return it receives json from django db as a message and renders frontend based on that json.
I have Invalid State Error when I try to send message by websocket, why? Messages send are usually Json. I works properly all the time but commented part doesn't and I don't know why please explain me.
function main() {
configGame();
}
function configGame() {
const socket = "ws://" + window.location.host + window.location.pathname;
const websocket = new WebSocket(socket);
const playerName = document.querySelector(".playerName_header").textContent;
function asignEvents() {
const ready_btn = document.querySelector(".--ready_btn");
const start_btn = document.querySelector(".--start_btn");
ready_btn.addEventListener("click", () => {
let mess = JSON.stringify({
player: playerName,
action: "ready",
});
sendMess(mess);
});
start_btn.addEventListener("click", () => {
let mess = JSON.stringify({
player: playerName,
action: "start",
});
sendMess(mess);
});
}
function openWebsocket() {
console.log("Establishing Websocket Connection...");
websocket.onopen = () => {
console.log("Websocket Connection Established!");
};
}
function setWebsocket() {
websocket.onmessage = (mess) => {
console.log(`Message: ${mess.data}`);
dataJson = JSON.parse(mess.data);
dataJson = JSON.parse(dataJson.message);
//Player Ready (jeszcze z max_players zrobic kontrolke)
if (dataJson.action === "player_ready") {
const playersReadyText = document.querySelector(".players_ready_text");
playersReadyText.textContent = `Players ready: ${dataJson.players_ready}`;
}
};
websocket.onclose = () => {
console.log("Websocket Connection Terminated!");
};
}
/*
function checkState() {
let mess = JSON.stringify({
player: playerName,
action: "game state",
});
sendMess(mess);
}
*/
function sendMess(messText) {
websocket.send(messText);
}
openWebsocket();
checkState(); //This one doesn't work
asignEvents();
setWebsocket();
}
// Asigning Event Listneres to DOM ELEMENTS
function asignEvents() {
const ready_btn = document.querySelector(".--ready_btn");
const start_btn = document.querySelector(".--start_btn");
ready_btn.addEventListener("click", () => {
console.log("Ready");
});
start_btn.addEventListener("click", () => {
console.log("Start");
});
}
main();
Error:
Console (Safari) returns InvalidState error and points to
method checkState and sendMess.
InvalidStateError: The object is in an invalid state.
Is the websocket connected?
sendMess(messText) {
if (websocket.readyState === WebSocket.OPEN) {
websocket.send(messText);
} else {
console.warn("websocket is not connected");
}
}
I try to use Deno ws to reload the document, but it will throw an error after second reload
Uncaught ConnectionReset: Socket has already been closed throw new Deno.errors.ConnectionReset("Socket has already been closed");
var ws = new WebSocket("ws://127.0.0.1:8080/ws")
ws.onopen = function () {
ws.send('ws open')
console.log('ws open');
}
ws.addEventListener("message", (e) => {
if (e.data === 'fileUpdate') {
// ws.send('close')
location.replace(location.href);
}
})
seem location.replace(location.href) raise an error
any solution?
The error is happening because you're sending a message after the socket is closed.
When you do: location.replace(location.href); the page is refreshed and the current socket is closed.
You can either catch the error, or check for ws.isClosed before sending the message.
for await (const e of ws) {
if (e === 'close') {
ob.remove("fileUpdate")
continue
}
ob.on("fileUpdate", () => {
console.log('sending')
if(!ws.isClosed)
ws.send("fileUpdate")
})
}
While that will fix the error, it won't fix the cause. Your ob.on('fileUpdate') event is firing after the socket is closed. You should clear that listener on the WebSocket close event, you can do that using ws.isWebSocketCloseEvent
import { acceptWebSocket, isWebSocketCloseEvent } from "https://deno.land/std#0.51.0/ws/mod.ts";
/* ... */
for await (const e of ws) {
if(isWebSocketCloseEvent(e) || e === 'close') {
// clear listeners here
ob.remove("fileUpdate")
// if e === 'close' you may want to close the socket
}
}
here is Deno code :
import { Application } from "https://deno.land/x/abc/mod.ts";
import { acceptWebSocket } from "https://deno.land/std#0.51.0/ws/mod.ts";
new Application()
.file("/", "./index.html")
.file("/module.js", "./module.js")
// .file("sw.js", "ServiceWorker.js")
.get('/ws', async (c: any) => {
const { conn, headers, r: bufReader, w: bufWriter } = c.request;
const ws = await acceptWebSocket({
conn,
headers,
bufReader,
bufWriter,
});
for await (const e of ws) {
if (e === 'close') {
ob.remove("fileUpdate")
continue
}
ob.on("fileUpdate", () => {
ws.send("fileUpdate")
})
}
})
.start({ port: 8080 })
ob like this :
class Ob {
private list: ObList[] = []
send(event: string) {
this.list.forEach((e: ObList) => {
if (e.event === event) {
e.cb && e.cb()
}
})
}
on(event: string, cb: Function) {
this.list.push({ event, cb })
}
remove(event:string){
this.list=this.list.filter((e:ObList)=>{
return e.event!==event
})
}
}
the framework is abc
I started integrating websockets into an existing React/Django app following along with this example (accompanying repo here). In that repo, the websocket interface is in websockets.js, and is implemented in containers/Chat.js.
I can get that code working correctly as-is.
I then started re-writing my implementation to use Hooks, and hit a little wall. The data flows through the socket correctly, arrives in the handler of each client correctly, and within the handler can read the correct state. Within that handler, I'm calling my useState function to update state with the incoming data.
Originally I had a problem of my single useState function within addMessage() inconsistently firing (1 in 10 times?). I split my one useState hook into two (one for current message, one for all messages). Now in addMessage() upon receiving data from the server, my setAllMessages hook will only update the client where I type the message in - no other clients. All clients receive/can log the data correctly, they just don't run the setAllMessages function.
If I push to an empty array outside the function, it works as expected. So it seems like a problem in the function update cycle, but I haven't been able to track it down.
Here's my version of websocket.js:
class WebSocketService {
static instance = null;
static getInstance() {
if (!WebSocketService.instance) {
WebSocketService.instance = new WebSocketService();
}
return WebSocketService.instance;
}
constructor() {
this.socketRef = null;
this.callbacks = {};
}
disconnect() {
this.socketRef.close();
}
connect(chatUrl) {
const path = `${URLS.SOCKET.BASE}${URLS.SOCKET.TEST}`;
this.socketRef = new WebSocket(path);
this.socketRef.onopen = () => {
console.log('WebSocket open');
};
this.socketRef.onmessage = e => {
this.socketNewMessage(e.data);
};
this.socketRef.onerror = e => {
console.log(e.message);
};
this.socketRef.onclose = () => {
this.connect();
};
}
socketNewMessage(data) {
const parsedData = JSON.parse(data);
const { command } = parsedData;
if (Object.keys(this.callbacks).length === 0) {
return;
}
Object.keys(SOCKET_COMMANDS).forEach(clientCommand => {
if (command === SOCKET_COMMANDS[clientCommand]) {
this.callbacks[command](parsedData.presentation);
}
});
}
backend_receive_data_then_post_new(message) {
this.sendMessage({
command_for_backend: 'backend_receive_data_then_post_new',
message: message.content,
from: message.from,
});
}
sendMessage(data) {
try {
this.socketRef.send(JSON.stringify({ ...data }));
} catch (err) {
console.log(err.message);
}
}
addCallbacks(allCallbacks) {
Object.keys(SOCKET_COMMANDS).forEach(command => {
this.callbacks[SOCKET_COMMANDS[command]] = allCallbacks;
});
}
state() {
return this.socketRef.readyState;
}
}
const WebSocketInstance = WebSocketService.getInstance();
export default WebSocketInstance;
And here's my version of Chat.js
export function Chat() {
const [allMessages, setAllMessages] = useState([]);
const [currMessage, setCurrMessage] = useState('');
function waitForSocketConnection(callback) {
setTimeout(() => {
if (WebSocketInstance.state() === 1) {
callback();
} else {
waitForSocketConnection(callback);
}
}, 100);
}
waitForSocketConnection(() => {
const allCallbacks = [addMessage];
allCallbacks.forEach(callback => {
WebSocketInstance.addCallbacks(callback);
});
});
/*
* This is the problem area
* `incoming` shows the correct data, and I have access to all state
* But `setAllMessages` only updates on the client I type the message into
*/
const addMessage = (incoming) => {
setAllMessages([incoming]);
};
// update with value from input
const messageChangeHandler = e => {
setCurrMessage(e.target.value);
};
// Send data to socket interface, then to server
const sendMessageHandler = e => {
e.preventDefault();
const messageObject = {
from: 'user',
content: currMessage,
};
setCurrMessage('');
WebSocketInstance.backend_receive_data_then_post_new(messageObject);
};
return (
<div>
// rendering stuff here
</div>
);
}
There is no need to rewrite everything into functional components with hooks.
You should decompose it functionally - main (parent, class/FC) for initialization and providing [data and] methods (as props) to 2 functional childrens/components responsible for rendering list and input (new message).
If you still need it ... useEffect is a key ... as all code is run on every render in functional components ... including function definitions, redefinitions, new refs, duplications in callbacks array etc.
You can try to move all once defined functions into useEffect
useEffect(() => {
const waitForSocketConnection = (callback) => {
...
}
const addMessage = (incoming) => {
setAllMessages([incoming]);
};
waitForSocketConnection(() => {
...
}
}, [] ); // <<< RUN ONCE