Working with websocket in ReactJS project - reactjs

I am tying to implement socket connection in ReactJs, I have read there documentation for how to implement more than one namespace in socket and this is how I did it after following there instructions
import {Manager} from "socket.io-client";
export const manager = new Manager(process.env.REACT_APP_SOCKET_API, {
transports: ["websocket"],
reconnection:true,
autoConnect:true,
});
// add the namespace [namespace1,namespace2]
export const chatSocket = manager.socket("/namespace1");
export const notesSocket = manager.socket("/namespace2");
manager.open((err) => {
if (err) {
console.log("socket connection error", err);
} else {
console.log("socket connection succeeded");
}
});
now I am trying to use it like that in my react app is this right or I should use it in another way because I don't see any result from my console.log mehtod
useEffect(() => {
chatSocket.on('connect',() => {
console.log('connected socket chat')
})
notesSocket.on('connect',() => {
console.log('connected socket notes')
})
},[])

Related

React/Socket.io chat app not working on heroku

I have a chat app I made using React for the frontend, DRF for the backend and I also have a node.js server within the React app for socket.io The issue is that the chat doesn't work basically (it works fine locally). When a message is sent it's not emitted and only shows up when I refresh since it's then pulled from the DB instead. I have gone through many threads on here for this issue but can't figure out what I'm doing wrong.
My server:
const server = require("http").createServer();
const io = require("socket.io")(server, {
cors: {
origin: "*",
},
});
const PORT = process.env.PORT || 5000;
const NEW_CHAT_MESSAGE_EVENT = "newChatMessage";
io.on("connection", (socket) => {
console.log('Client connected')
// Join a conversation.
const {roomId} = socket.handshake.query;
socket.join(roomId);
// Listen for new messages
socket.on(NEW_CHAT_MESSAGE_EVENT, (data) => {
io.in(roomId).emit(NEW_CHAT_MESSAGE_EVENT, data);
});
// Leave the room if the user closes the socket
socket.on("disconnect", () => {
socket.leave(roomId);
});
});
server.listen(PORT, (error) => {
if (error) throw error;
console.log(`Listening on port ${PORT}`);
});
Hook I made for the frontend:
const NEW_CHAT_MESSAGE_EVENT = "newChatMessage"; // Name of the event
const SOCKET_SERVER_URL = `https://<my-react-frontend>.herokuapp.com`;
export const useChat = () => {
const socketRef = useRef();
const {messages, setMessages, activeConvo, headerConvo, reloadSideBar, setReloadSideBar} = useActiveConvo()
const roomId = activeConvo
useEffect(() => {
console.log('useChat useEffect ran')
// Creates a WebSocket connection
socketRef.current = socketIOClient(SOCKET_SERVER_URL, {
query: {roomId},
});
// Listens for incoming messages
socketRef.current.on(NEW_CHAT_MESSAGE_EVENT, (message) => {
const incomingMessage = {
message: message.body,
created_by: localStorage.getItem('currentUserID'),
};
console.log('messages set in useChat useFfect')
setMessages((messages) => [...messages, incomingMessage]);
});
// Destroys the socket reference
// when the connection is closed
return () => {
socketRef.current.disconnect();
};
}, [roomId]);
// Sends a message to the server that
// forwards it to all users in the same room
const sendMessage = (messageBody) => {
socketRef.current.emit(NEW_CHAT_MESSAGE_EVENT, {
body: messageBody,
senderId: socketRef.current.id,
});
const fetchContents = {
message: messageBody,
created_by: localStorage.getItem('currentUserID'),
convo_id: activeConvo ? activeConvo : headerConvo
}
fetch(`https://<my-drf-backend>.herokuapp.com/api/save-message/`, authRequestOptions(('POST'), fetchContents))
.then(response => response.json())
.then(setReloadSideBar(reloadSideBar + 1))
.catch(error => console.log(error))
};
return {messages, sendMessage};
};
The issue in most of the threads appeared to be either still using the localhost url on the frontend or not using process.env.PORT in the server but it's still busted after fixing that. I also saw someone mention in another thread that the folder structure was the issue so I tried having the server file in the root of the react app and having it in it's own folder under "src", no dice.
In case anyone faces this same issue, I solved it by putting the server in a separate app on heroku

Socket.io and Next.Js

I am working on a project for an interview and have been asked to create a NextJS app using Socket.io for realtime chat. I have the chat functionality working, but one of my requirements is to have an area where a user can see a list of current users. While I've found examples for Express servers, I cannot seem to work out how to do this using Next's API system. I have two connected issues:
Maintaining a list of users with chosen display names (not just the socket id)
Accessing and returning a current user list whenever a user joins or leaves.
I haven't had any luck scanning the docs.
Here is the server function:
import { NextApiRequest } from 'next';
import { NextApiResponseServerIO } from '../../types/next';
import { Server as ServerIO } from 'socket.io';
import { Server as NetServer } from 'http';
export const config = {
api: {
bodyParser: false
}
}
export default async (req: NextApiRequest, res: NextApiResponseServerIO) => {
if (!res.socket.server.io) {
console.log("** New Socket.io server **")
// adapts the Next net server to http server
const httpServer: NetServer = res.socket.server as any;
const io = new ServerIO(httpServer, {
path: '/api/socketio'
})
io.on('connect', async (socket) => {
socket.join('main')
// where I plan to put the code to send a current list
})
io.on('disconnect', socket => {
socket.leave('main')
})
res.socket.server.io = io;
}
res.end();
}
And the related client code:
useEffect((): any => {
const url = process.env.NEXT_BASE_URL as string | "";
// connect to socket server
const socket = io(url, {
path: "/api/socketio",
});
// log socket connection
socket.on("connect", () => {
dispatch(connect(socket.id));
dispatch(updateId(socket.id))
});
//updates chat on message dispatch
socket.on("message", (message: IMsg) => {
dispatch(receive(message));
});
socket.on('updateUsersList', (users) => {
console.log("Is this the users", users)
})
//server disconnect on unmount
if (socket) return () => dispatch(disconnect(socket));
}, []);

How to avoid reconnections with socket.io-client and React app?

I tried to connect React client to my Socket.IO server. I noticed Socket.IO client reconnects every +/- 5s. When I try do the same thing with vanilla html/js simple app everything works crrectly.
Inside React component:
useEffect(() => {
const s = getChatClient();
}, []);
Inside socket.io-client module:
var chatClient;
export function getChatClient(
token = "secret"
) {
if (!chatClient) {
chatClient = io("http://localhost:5000/chat", {
query: {
token,
},
});
chatClient
.on("connect", () => {
chatClient.emit("textMessage", "123cos");
})
.on("hello", (msg) => {
console.log("12");
});
}
return chatClient;
}
BTW: I know I can do it export const etc (I've changed to this version becouse I thought it'll help).
I tried different ways to resolve this problem, but I got in stuck. Any ideas?
Log from the server when I open html/js simple client:
15:30:00 User Ilona connected to the socket.io server on /
and when I quit:
15:29:12 User Ilona disconnected
With React App:
15:30:00 User Ilona connected to the socket.io server on '/'
15:30:05 User Ilona disconnected
15:30:10 User Ilona connected to the socket.io server on '/'
15:30:15 User Ilona disconnected
15:30:20 User Ilona connected to the socket.io server on '/'
15:30:25 User Ilona disconnected
The problem isn't related with component rerender or something like this.
I'm working on MacOS Big Sur.
Consider creating, and then consuming from a context:
SocketContext.jsx :
import { createContext, useState } from 'react';
export const SocketContext = createContext();
export default function SocketContextProvider(props) {
const [sock, setSocket] = useState(null);
let socket = async () => {
if (sock) {
return Promise.resolve(sock); // If already exists resolve
}
return new Promise((resolve, reject) => {
let newSock = io('URL'),
{
query: {
// Options
},
}; // Try to connect
let didntConnectTimeout = setTimeout(() => {
reject();
}, 15000) // Reject if didn't connect within 15 seconds
newSock.once('connect', () => {
clearTimeout(didntConnectTimeout); // It's connected so we don't need to keep waiting 15 seconds
setSocket(newSock); // Set the socket
resolve(newSock); // Return the socket
})
});
};
return (
<SocketContext.Provider value={{ socket }}>
{props.children}
</SocketContext.Provider>
);
}
Component.jsx :
import { useContext, useState, useEffect } from 'react';
import { SocketContext } from './SocketContext.jsx';
export default function MyComponent() {
const { socket } = useContext(SocketContext);
const [sock, setSock] = useState(null);
useEffect(() => {
socket()
.then((resultSocket) => setSock(resultSocket))
.catch(() => {
/* Catch any errors here */
console.log('Couldn\'t connect the socket!')
});
}, []);
return (
<div>
<code>I'm a context consumer...</code>
</div>
);
}

React native custom host pusher connection

Can't seem to connect to pusher. I've been working in these for days and can't seem to connect to pusher. version 6.0.3
import Pusher from 'pusher-js/react-native';
componentDidMount() {
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => {
try {
this.pusher = new Pusher('key', {
wsHost: 'host',
wsPort: 6001,
enabledTransports: ['ws'],
disableStats:true,
});
Pusher.log = (msg) => {
console.log('Pusher message ' + msg);
};
} catch (error) {
console.log('error', error)
}
this.group_channel = this.pusher.subscribe('groupchat.' + session_id);
this.pusher.connection.bind('GroupMessageSent', (data) => {
console.log('connected', data)
});
this.group_channel.bind('GroupMessageSent', (data) => {
console.log('data', data)
});
})
}
Pusher message Pusher : : ["Connecting",{"transport":"xhr_polling","url":"https://sockjs-4444.pusher.com:443/pusher/app/DOCKOTO_KEY?protocol=7&client=js&version=6.0.3"}]
https://snack.expo.io/#jsfit/pusher
I have checked the pusher is working fine for me on the localhost
the main reason I think is your ws ws://test.dockoto.com:6001
Laravel WebSockets default port 6001 as you know but you still need to open the port on the server.
Shared hosting I think you have to contact with you hosting provider to open the port if needed
EC2:
https://stackoverflow.com/a/56667321/13647574

socket.on function is not being called for the first time when react component mounted

I am trying to create a simple real time chat app to practise with socket-io. I am using React to create UI and i am using NodeJS with Express web server. I have ChatPage React component to show messages.
I am emitting an event newMessage when a user joined to a room and i am sending a welcome message as data from server to client. At that time i am routing to ChatPage component and i am subscribing to updateUserList event in componentWillMount but this event is not coming up to that callback i defined. After some trials i realized a weird thing about this event. I subscribed this event from another .js file and i could see data that i sent from server.
// server.js
socket.on('joinRoom', (data, callback) => {
const { username, roomName } = data
if (username === '' || roomName === '') {
if (callback) {
return callback({ error: 'Username and password required!' })
}
return
}
socket.join(roomName, (err) => {
if (err && callback) {
return callback({ error: 'Could not connect to room.' })
}
socket.emit('newMessage', generateMessage('Admin', 'Welcome to chat app'))
if (callback) {
callback()
}
})
})
// ChatPage.js
componentWillMount() {
const socket = io('http://localhost:3400')
socket.on('newMessage', (message) => {
console.log('newMessage', message)
})
}
// event is coming to here instead!
import socketIOClient from "socket.io-client";
export const getSocketIOClient = () => {
const endpoint = "http://localhost:3400"
const socket = socketIOClient(endpoint)
socket.on('newMessage', (er) => {
console.log('newMessage', message)
})
return socket;
}
I faced similar porblem in my project. There I noticed few things which are missing in your code.
You need to call back join room in client code as well in componentWillMount() method.
You need to initialize socket in state variable. I don't know exactly why it doesn't work when we initialize in const variable but it works when we use state variable instead. I verified this code, it works fine even in the first time react component mount.
Here, is sample code:
import React from 'react'
import io from 'socket.io-client';
class SocketExample extends React.Component {
state = {
chatMessage: '',
chatMessages: [],
socket: io.connect(`http://localhost:3001`),
}
submitChatMessage = (e) => {
e.preventDefault()
if (this.state.chatMessage.length > 0) {
this.state.socket.emit('newMessage', {
username: this.props.currentUser.username,
message: this.state.chatMessage,
});
this.setState({
chatMessage: '',
})
}
}
componentDidMount(){
this.state.socket.on('connect', () => {
this.state.socket.emit('join', { uniqueValue: chatRoomName });
});
this.state.socket.on("newMessage", data => {
this.setState({
chatMessages: [...this.state.chatMessages, data],
});
});
}
render() {
return (
<div>{this.state.chatMessages.map(chat => {
return <p>{chat.username + ': ' + chat.message}</p>
})}</div>
)
}
}
export default SocketExample

Resources