There is a two different component. One is patient and another is Doctor. The doctor can be on any page. When patient fires join event from his page to a particular doctor(e.g abc), the node server will then catch that event and will send JoinAccept event to 'abc doctor'. Following doctor should get notification that following patient wants to connect with you.
To achieve this, I could able to send 'Join' event up to server but could not able to listen JoinAccept event on doctor's end.
This is what I have done
context.js
import io from "socket.io-client";
import { SOCKET_URL } from "constants/url";
const SocketContext = React.createContext();
const SocketProvider = ({ children }) => {
const [socketClient, setSocketClient] = React.useState();
const [socketUpdated, setSocketUpdated] = React.useState(false);
React.useEffect(() => {
const socket = io(SOCKET_URL);
setSocketClient(socket);
return () => {
io.disconnect();
};
}, []);
React.useEffect(() => {
console.log("socketClient", socketClient);
if (socketClient) {
const tokenData =
!!localStorage.token &&
JSON.parse(atob(localStorage.token.split(".")[1]));
if (tokenData.user) {
console.log("user", tokenData.user);
socketClient.emit("clientData", tokenData.user);
socketClient.on("connected", msg => {
console.log("connected");
setSocketUpdated(true);
});
// setSocketUpdated(true);
}
}
}, [socketClient]);
console.log("socket updated", socketUpdated);
return (
<>
<SocketContext.Provider value={{ socket: socketClient, socketUpdated }}>
{children}
</SocketContext.Provider>
</>
);
};
export { SocketContext, SocketProvider };
Patient.js (it will fire Join event)
const Patient = () => {
const { socket } = React.useContext(SocketContext);
React.useEffect(() => {
const data = {
to: "abcdoctor#gmail.com",
from: "patient#gmail.com",
message: "Join a call"
};
socket.emit("Join", data);
}, [socket]);
return (
<div>
<h1>Patient</h1>
</div>
);
};
Doctor.js
const DoctorParentComponent = () => {
return (
<>
<SocketProvider>
<h1>Navbar</h1>
<DoctorRoutes />
</SocketProvider>
</>
);
};
DoctorPage.js
const DoctorPage = () => {
const [msg, setMessage] = React.useState("");
const { socket, socketUpdated } = React.useContext(SocketContext);
console.log("Doctor socket", socket, socketUpdated);
React.useEffect(() => {
console.log("socket", socket);
if (socket !== undefined) {
console.log("socket join", socket);
socket.on("JoinAccept", message => {
debugger;
console.log("message", message);
setMessage(message);
});
}
}, [socket]);
return (
<div>
<h1>Doctor </h1>
</div>
);
};
server.js
io.of("/sockets").on("connection", socket => {
console.log("socket connection is made!!!", socket.id);
socket.on("clientData", clientData => {
console.log(clientData, "CLEITN DATA");
socket.emit("connected", "connected");
});
socket.on("Join", data => {
const msg = {
message: "I am joining"
};
console.log("socket", socket.id);
console.log("I am Joining", data);
socket.broadcast.to(data.to).emit("JoinAccept", msg);
});
}
In my case the server gets Join event from patients and then sends event to requested doctor but doctor page is unresponsive. I mean doctor page does not listens the changes i.e it could not listens socket event JoinAccept so that it can join patient's request.
UPDATE
changing
socket.on("Join", data => {
const msg = {
message: "I am joining"
};
console.log("socket", socket.id);
console.log("I am Joining", data);
socket.broadcast.to(data.to).emit("JoinAccept", msg);
});
to following works
socket.on("Join", data => {
const msg = {
message: "I am joining"
};
console.log("socket", socket.id);
console.log("I am Joining", data);
io.of("/sockets").emit("JoinAccept", msg)
});
But I want to emit 'JoinAccept' event only for a particular doctor which patient has requested for from join events.
On DoctorPage.js socket is an object, if you put this object as the only variable in the dependency array it's going to run as a loop.
Try switching to the socket id instead, so:
React.useEffect(() => {
console.log("socket", socket);
if (socket !== undefined) {
console.log("socket join", socket);
socket.on("JoinAccept", message => {
debugger;
console.log("message", message);
setMessage(message);
});
}
}, [socket.id]);
Related
I'm trying to build a chat application,
The issue is whenever I send a emit an message to socket.io at the same time I recieve the same message two times. I got same message console.log two times.
Now this is my Server.js (Using express & Socket.io)
const express = require("express");
const app = express();
const http = require("http");
const cors = require("cors");
const { Server } = require("socket.io");
app.use(cors());
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
},
});
io.on("connection", (socket) => {
console.log(`User Connected: ${socket.id}`);
socket.on("join_room", (data) => {
socket.join(data);
console.log(`User with ID: ${socket.id} joined room: ${data}`);
});
socket.on("send_message", (data) => {
socket.to(data.room).emit("receive_message", data);
});
socket.on("disconnect", () => {
console.log("User Disconnected", socket.id);
});
});
server.listen(3001, () => {
console.log("SERVER RUNNING");
});
And this is my client's chat.js where I am sending prop of
const socket = io.connect("http://localhost:3001");
below is component
In this Chat component.
import React, { useEffect, useState } from "react";
function Chat({ socket, username, room }) {
useEffect(() => {
socket.on("receive_message", (data) => {
console.log(data);
});
return () => {
socket.off("receive_message", (data) =>
console.log(`receive_message off ${data}`)
);
};
}, [socket]);
return (
<>
<input
type="text"
placeholder="John..."
onChange={(event) => {
console.log(event.target.value);
}}
onKeyPress={(event) => {
if (event.key === "Enter") {
socket.emit("send_message", {
room: room,
author: username,
message: event.target.value,
});
}
}}
/>
</>
);
}
export default Chat;
After opening second tab I see two console.logs popping up with the same message. Can anybody tell me what I am doing wrong here or missing here?
I have tried removing React.StrictMode from index.js, it resolves the issue but I don't want to remove it. Also after shifting my react version from 18 to 17 it also resolves the issue, how I can tackle this issue in 18. Also I want to cover that issue in same chat.js component. Any help would be appreciated.
Thanks.
The problem is here:
useEffect(() => {
socket.on("receive_message", (data) => {
console.log(data);
});
return () => {
socket.off("receive_message", (data) =>
console.log(`receive_message off ${data}`)
);
};
}, [socket]);
You are properly trying to remove a listener on the return, but the problem is that you are removing the wrong listener--you have to pass the exact same function that you want to remove (because you can have two listeners on an event). This means that when the second render occurs during safe mode, you have two listeners added to that event.
Try this:
useEffect(() => {
const receiveMessage = (data) => {
console.log(data)
}
socket.on("receive_message", receiveMessage);
return () => {
socket.off("receive_message", receiveMessage);
};
}, [socket]);
That will make sure that the right function is removed. If this works, please make sure to mark my answer!
I have a node.js socket.io server that emits two messages with a 1000 ms delay. The messages are logged fine in the react app, however if I remove the 1000 ms delay, react does not update the first message's state before the second one is received.
The only way I was able to fix this issue was by using a global variable to store the first message. Is there a solution without a global variable?
Server code:
const app = express();
const http = require("http");
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server, {
cors: {
origin: "*",
},
});
io.on("connection", (socket) => {
socket.emit("data1", "First data");
setTimeout(() => {
socket.emit("data2", "Second data");
}, 1000);
});
server.listen(3001, () => {
console.log("listening on *:3001");
});
Working react app using global variable:
var data1;
export default function Main() {
const socket = useContext(SocketContext);
const onData1 = useCallback((data) => {
console.log("Received data1:", data);
data1 = data;
}, []);
const onData2 = useCallback(
(data) => {
console.log("Received data2:", data);
console.log("Data1 is:", data1);
},
[data1]
);
useEffect(() => {
console.log("Binding socket listeners...");
socket.on("data1", onData1);
socket.on("data2", onData2);
return () => {
console.log("Unbinding socket listeners...");
socket.off("data1", onData1);
socket.off("data2", onData2);
};
}, [socket, onData1, onData2]);
return <></>;
}
Not working react app:
export default function Main() {
const socket = useContext(SocketContext);
const [data1, setData1] = useState("");
const onData1 = useCallback((data) => {
console.log("Received data1:", data);
setData1(data);
}, []);
const onData2 = useCallback(
(data) => {
console.log("Received data2:", data);
console.log("Data1 is:", data1);
},
[data1]
);
useEffect(() => {
console.log("Binding socket listeners...");
socket.on("data1", onData1);
socket.on("data2", onData2);
return () => {
console.log("Unbinding socket listeners...");
socket.off("data1", onData1);
socket.off("data2", onData2);
};
}, [socket, onData1, onData2]);
return <></>;
}
I've been trying to develop a realtime chat app using the MERN stack (React-Native instead of React) and was successful, but I cannot convert it to real time using the socket.io library. Below I have provided some code from my project that describes the situation/structure before using socket.io and after:
FLOW
A user registers/login using his phone number, on every first render of home screen, I will fetch the rooms, if the user has any. (rooms => conversations with people, like homescreen of whatsapp). Each room will have a unique roomId and data of these 2 users (refer to the Room schema). Now when he/she will tap to enter the ChatScreen, users can send message (refer Message schema) to eachother. To fetch all messages of a particular chat, I make use of the unique roomId to fetch all the messages having that roomId only. Now, the problem is, when other user sends any message, I have to re-render the whole app to get new messages, therefore, no real-timeness.
SERVER
I have 3 collections in my mongodb, 1) users, 2) rooms 3) messages.
schemas:
const RoomSchema = mongoose.Schema({
roomId: String,
usersId: [String],
users: Object,
}, {
timestamps: true,
});
module.exports = mongoose.model('Room', RoomSchema);
const MessageSchema = mongoose.Schema({
roomId: String,
senderId: String,
text: String,
}, {
timestamps: true,
});
module.exports = mongoose.model('Message', MessageSchema);
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
phone: {
type: String,
required: true,
trim: true,
unique: true,
},
otp: String,
}, {
timestamps: true,
});
module.exports = mongoose.model('User', UserSchema);
index.js
const express = require('express');
const mongoose = require('mongoose');
const http = require('http');
const socketio = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketio(server).sockets;
mongoose.connect(process.env.MONGO_URL);
mongoose.connection.on('connected', () => {
console.log('[INFO] Connected to MongoDB');
});
mongoose.connection.on('error', (error) => {
console.log(`[ERROR] ${error}`);
});
// SOCKETS MIDDLEWARE
require('./middlewares/socket')(io);
server.listen(process.env.PORT, () => {
console.log(`[INFO] Server running at ${process.env.PORT}`);
});
socket.js
const Message = require('../models/Message');
module.exports = (io) => {
io.on('connection', (socket) => {
console.log('A user connected.');
socket.on('sent_messages', async ({ roomId }, cb) => {
const messages = await Message.find({roomId});
cb(messages);
});
socket.on('send2user', async (data) => {
socket.broadcast.to(data.roomId).emit();
const message = new Message({
roomId: data.roomId,
senderId : data.senderId,
text: data.text,
});
const result = await message.save();
});
});
};
Now, basically, whenever a user touch on a contact item in frontend, a room will be created (chat room) for these 2 users (private 1-1 chat app). So, now the 2 users are ready to chat real time. The endpoints for fetching and creating messages (Although, I have created a new message in socket.js file but don't know how to proceed further):
router.post('/create_message', async (req, res) => {
const {roomId, senderId, text} = req.body;
try {
const message = new Message({
roomId,
senderId,
text,
});
const result = await message.save();
return res.status(200).json({
type: 'success',
data: result,
});
} catch (error) {
return res.status(422).send({error: `${error.message}`});
}
});
router.post('/get_messages', async (req, res) => {
const {roomId} = req.body;
try {
const messages = await Message.find({roomId});
return res.status(200).json({
type: 'success',
data: messages,
});
} catch (error) {
return res.status(422).send({error: `${error.message}`});
}
});
FRONTEND
utility.js
export const socket = io(API_URL, {forceNew: true});
socket.on('connection', () => {
console.log('Connected to server');
});
export const fetchMessages = (roomId, setMessages) => {
// socket.emit('sent_messages', {roomId}, (data) => {
// setMessages(data);
// });
AsyncStorage.getItem('token')
.then(token => {
if (token) {
fetch(`${API_URL}/message/get_messages`, {
method: 'POST',
headers: {
...
},
body: JSON.stringify({roomId}),
})
.then(response => response.json())
.then(data => {
if (data.type === 'success') {
setMessages(data.data);
}
if (data.error) {
console.log(data.error);
}
})
.catch(error => {
console.log('[ERROR] While fetching messages: ' + error.message);
});
} else {
console.log('token is null');
}
})
.catch(error => {
console.log('[ERROR] While fetching token: ' + error.message);
});
};
export const createMessage = (message, setMessages) => {
AsyncStorage.getItem('token')
.then(token => {
if (token) {
fetch(`${API_URL}/message/create_message`, {
method: 'POST',
headers: {
...
},
body: JSON.stringify(message),
})
.then(response => response.json())
.then(data => {
if (data.type === 'success') {
const latestMessage = data.data;
setMessages((prevMessages) => ([
...prevMessages,
latestMessage,
]));
// socket.emit('send2user', latestMessage);
}
if (data.error) {
console.log(data.error);
}
})
.catch(error => {
console.log('[ERROR] While fetching messages: ' + error.message);
});
} else {
console.log('token is null');
}
})
.catch(error => {
console.log('[ERROR] While fetching token: ' + error.message);
});
};
ChatScreen.js
const ChatScreen = () => {
const {params} = useRoute();
const roomId = params?.roomId;
const navigator = useNavigation();
const {user, rooms} = useAuth();
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const sendMessage = () => {
if (input) {
const message = {
roomId,
senderId: user._id,
text: input,
};
createMessage(message, setMessages);
setInput('');
}
};
useEffect(() => {
fetchMessages(roomId, setMessages);
}, []);
const scrollViewRef = useRef();
return (
<SafeAreaView>
<KeyboardAvoidingView>
<>
{/* RENDER MESSAGES WITH SCROLLVIEW */}
<ScrollView
ref={scrollViewRef}
onContentSizeChange={() =>
scrollViewRef.current.scrollToEnd({animated: true})
}
onLayout={() =>
scrollViewRef.current.scrollToEnd({animated: true})
}>
{messages.length > 0 ? (
messages.map((message, index) => (
<MessageItem key={index} myID={user._id} data={message} />
))
) : (
<Text>Start Chatting</Text>
)}
</ScrollView>
<View>
<View>
<TextInput
value={input}
onChangeText={setInput}
placeholder="Type here"
/>
</View>
<TouchableOpacity
onPress={sendMessage}>
<IonIcon name="ios-add" size={28} color="#fff" />
</TouchableOpacity>
</View>
</>
</KeyboardAvoidingView>
</SafeAreaView>
);
};
export default ChatScreen;
So, as you can see, I cannot make it real time using socket.io and I'm also confused whether to fetch/create messages using socket or api endpoint. If someone could help me in this problem, I would really appreciate it! I just want to make this work like a real time chat app using socketio.
UPDATE:
By watching some tutorials & documentation of socket.io, I tried fetch and post a message and receive real-time update, but again, failed. So, below are the changes that I made:
node server socket.js
const Message = require('../models/Message');
module.exports = (io) => {
io.on('connection', (socket) => {
console.log('A user connected.');
socket.on('get_messages', async (roomId) => {
const messages = await Message.find({roomId});
socket.broadcast.to(roomId).emit('get_messages', messages);
});
socket.on('listener', async (data) => {
// here, I am able to receive `data` : {roomId: '...', senderId: '...', text: '...'}
io.to(data.roomId).emit('listener', data); // but using this, I cannot listen to changes, look in client file below this.
const message = new Message({
roomId: data.roomId,
senderId : data.senderId,
text: data.text,
});
await message.save();
});
});
};
client - react - ChatScreen.js:
import {socket} from '../../utils/utility';
const ChatScreen = () => {
const {params} = useRoute();
const roomId = params?.roomId;
const navigator = useNavigation();
const {user, rooms} = useAuth();
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const InitialFetchMessages = (_roomId) => {
socket.on('get_messages', () => {
socket.emit('get_messages', ); // HOW TO RECEIVE THE MESSAGES HERE?
});
};
const sendMessage = () => {
if (input) {
const message = {
roomId,
senderId: user._id,
text: input,
};
socket.emit('listener', message);
setInput('');
}
};
// main real-time listener
useEffect(() => { // NOT RUNNING
socket.on('listener', (data) => { // data = received data
setMessages([...messages, data]);
});
return () => socket.off('listener');
}, [messages]);
useEffect(() => {
InitialFetchMessages(roomId, setMessages);
}, []);
const scrollViewRef = useRef();
return (
<SafeAreaView>
<KeyboardAvoidingView>
<>
{/* RENDER MESSAGES WITH SCROLLVIEW */}
<ScrollView
ref={scrollViewRef}
onContentSizeChange={() =>
scrollViewRef.current.scrollToEnd({animated: true})
}
onLayout={() =>
scrollViewRef.current.scrollToEnd({animated: true})
}>
{messages.length > 0 ? (
messages.map((message, index) => (
<MessageItem key={index} myID={user._id} data={message} />
))
) : (
<Text>Start Chatting</Text>
)}
</ScrollView>
<View>
<View>
<TextInput
value={input}
onChangeText={setInput}
placeholder="Type here"
/>
</View>
<TouchableOpacity
onPress={sendMessage}>
<IonIcon name="ios-add" size={28} color="#fff" />
</TouchableOpacity>
</View>
</>
</KeyboardAvoidingView>
</SafeAreaView>
);
};
export default ChatScreen;
Can anyone point out what is going on and why it is not emitting to roomId because I want it to be private and based on roomId.
In frontend, you're using API to send a message instead of that use Socket on and emit to be real time. Since you're using API so chat will update only if it's refreshed.
I'm trying to make a call from client side(browser) to FreeSWITCH server using this libs:
Sip.js
Sip.js framework
And also I use React.
When I make a call I successfully invite destination URI.
After click button and call function callSip our SessionState changes to Ringing, then successful invitation, then SessionState is "Answered" and after that the function onAccept in requestDelegate object is run, but no connection is established because the SessionState goes to "Ended"
After all I have this log.
How to establish a voice connection so that it does not break off after receiving a call?
How to add a remote sound from sip to an audio ref element?
const CallList: React.FC<Props> = (props) => {
const [userURI, setUserURI] = useState<URI | null>(null);
const audioRef = useRef(null);
useEffect(() => {
const uri = UserAgent.makeURI("sip:9012#serverpoint");
if (!uri) {
throw new Error("Failed to create URI");
}
setUserURI(uri);
return () => {
console.log("unmount");
};
}, []);
if (!userURI) {
return <div></div>;
}
const userAgentOptions: UserAgentOptions = {
uri: userURI,
authorizationPassword: "pasw",
authorizationUsername: "9012",
transportOptions: {
server: "ws://serverpoint",
},
};
const userAgent = new UserAgent(userAgentOptions);
const target = UserAgent.makeURI("sip:9005#serverpoint");
const session = new Inviter(userAgent, target as URI);
// Setup session state change handler
session.stateChange.addListener((newState: SessionState) => {
switch (newState) {
case SessionState.Establishing:
alert("Ringing");
break;
case SessionState.Established:
alert("Answered");
break;
case SessionState.Terminated:
alert("Ended");
break;
}
});
// Options including delegate to capture response messages
const inviteOptions: InviterInviteOptions = {
requestDelegate: {
onAccept: (response) => {
console.log(response.message);
alert("Positive response");
console.log("Positive response = " + response);
},
onReject: (response) => {
console.log("Negative response = " + response);
},
},
sessionDescriptionHandlerOptions: {
constraints: {
audio: true,
video: false,
},
},
};
const callSIP = () => {
session
.invite(inviteOptions)
.then((request: OutgoingInviteRequest) => {
alert("Successfully sent INVITE");
alert("INVITE request = " + request);
})
.catch((error: Error) => {
console.log("Failed to send INVITE");
});
};
const stop = () =>
setTimeout(() => {
userAgent
.stop()
.then(() => {
console.log("Stopped");
})
.catch((error) => {
console.error("Failed to stop");
});
}, 5000);
userAgent
.start()
.then(() => {
console.log("Connected");
const registerer = new Registerer(userAgent);
registerer.stateChange.addListener((newStat) => {
console.log(newStat);
// switch (newState) {
// case RegistererState.Registered:
// console.log("Registered");
// break;
// case RegistererState.Unregistered:
// console.log("Unregistered");
// break;
// case RegistererState.Terminated:
// console.log("Terminated");
// break;
// }
});
registerer
.register()
.then((request) => {
alert("Successfully sent REGISTER");
alert("Sent request = " + request);
})
.catch((error) => {
console.error("Failed to send REGISTER");
});
if (registerer.state === "Registered") {
// Currently registered
alert("Registered");
}
// stop();
})
.catch((error) => {
console.error("Failed to connect");
});
const panes = [
{
menuItem: "Calls",
render: () => (
<Tab.Pane loading={false}>
<List celled>
<List.Item>
<audio ref={audioRef} id="remote-audio"></audio>
</List.Item>
{props.data.map((item) => (
<List.Item key={v4()} onClick={callSIP}>
<Image avatar src="" />
<List.Content>
<List.Header>
{item.location} - {item.number}
</List.Header>
{item.timestamp}
</List.Content>
<List.Content floated="right">
<Button>Call</Button>
</List.Content>
</List.Item>
))}
</List>
</Tab.Pane>
),
},
{
menuItem: "tab 2",
render: () => <Tab.Pane>Tab 2 Content</Tab.Pane>,
},
];
return (
<Container>
<Tab panes={panes} />
</Container>
);
};
export default CallList;
After session is established you can set medias, example: https://github.com/onsip/SIP.js/blob/master/src/platform/web/simple-user/simple-user.ts#L750
I'm working on a react app and after following a tutorial on rooms I can not longer get the input to clear when the message is inputed. The commented out sections are things I've tried. I think what I want to do us somehow refer to messageRef in the socket.js file and set it to an empty string. Alternatively I think I might want to move the room functions to Chat.js but the way I learned to set it up was in a seperate file.
For Clientside I have this Chat.js component and the socket.js in the utils folder
import React, { useEffect, useState, useRef } from "react";
import {initiateSocket, disconnectSocket, subscribeToChat, handleTyping, sendMessage } from "../../utils/socket/socket";
import "./listStyle.css";
var user = "User";
var store = "Store"
var storeMessage = user + " is going to " + store;
function Chat() {
const rooms = ['1', '2'];
let messageRef = useRef();
const [room, setRoom] = useState(rooms[0]);
const [typing, setTyping] = useState("")
const [message, setMessage] = useState("");
const [chat, setChat] = useState([]);
//keeps inputing for every letter
// const clearInput = (input) =>
// {
// //input = "";
// console.log("input cleared")
// }
// const handleSendMessage = () => {
// sendMessage(room, user + ": " + messageRef.current.value)
// messageRef.current.value = "";
// }
//const handleTyping = () => { socket.emit("typing", user + ": is typing") }
useEffect(() => {
if (room) initiateSocket(room);
subscribeToChat((err, data) => {
if (err) return;
setChat(oldChats => [...oldChats, data])
setTyping("")
});
return () => {
disconnectSocket();
}
}, [room]);
return (
<div>
<h1>Group: {room}</h1>
{ rooms.map((r, i) =>
<button onClick={() => setRoom(r)} key={i}>{r}</button>)}
<h1>{storeMessage}</h1>
<div id="list-chat">
<div id="chat-window">
<div id="output" >
{chat.map((m, i) => <p key={i}>{m}</p>)}
</div>
<div id="feedback" >{typing}</div>
</div>
<input id="message" autoComplete="off" type="text"
defaultValue = ""
placeholder="message"
//value={message}
onChange={() => setMessage(messageRef.current.value)} ref={messageRef}
//onChange={() => handleTyping(room, user + ": is typing.")}
/>
<button id="send"
onClick={() => sendMessage(room, user + ": " + messageRef.current.value)}
//onClick={() => console.log("test")}
//onClick={() => sendMessage(room, user + ": " + messageRef.current.value)}
//onchange={e => messageRef.current.value = ""}
//onClick={handleSendMessage()}
//onChange={() => handleTyping(room, user + ": is typing." )}
//only clears current room
//have to comment out on startup
//onChange={console.log("test")}
//onSubmit={messageRef.current.value = ""}
>Send</button>
</div>
</div>
)
}
export default Chat;
socket.js
The message is sent using the sendMessage function
(these 3 are from before rooms were implemented
// import {io} from "socket.io-client";
// const socket = io.connect(process.env.PORT || "http://localhost:3001");
// export default socket;
import io from 'socket.io-client';
let socket;
export const initiateSocket = (room) => {
socket = io('http://localhost:3000');
console.log(`Connecting socket...`);
if (socket && room) socket.emit('join', room);
}
export const disconnectSocket = () => {
console.log('Disconnecting socket...');
if(socket) socket.disconnect();
}
export const subscribeToChat = (cb) => {
if (!socket) return(true);
socket.on('chat', msg => {
console.log('Websocket event received!');
return cb(null, msg);
});
}
export const handleSendMessage = () =>{
}
export const sendMessage = (room, message) => {
if (socket) socket.emit('chat', {room, message });
console.log("message sent")
//message = "";
}
export const handleTyping = (room, message) =>{
if (socket) socket.emit('typing', {room, message});
//message = "";
console.log("typing")
}
and my server side file (seperate from the actual server.js)
const socketio = require('socket.io');
//let rooms = ["room1", "room2"]
const initializeSocketio = (server) => {
const io = socketio(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
allowedHeaders: ["my-custom-header"],
credentials: true
}
});
io.on('connection', function (socket) {
console.log("made socket connection", socket.id)
// socket.on('chat', function (data) {
// io.sockets.emit('chat', data)
// });
//doesn't do anything right now
// socket.on('start-shop', function (data) {
// io.sockets.emit('start-shop', data)
// });
// socket.on('typing', function (data) {
// socket.broadcast.emit('typing', data)
// });
//room test
// room1 = "room1";
// socket.on('join1', function (room1) {
// //socket.join(room1);
// console.log(room1)
// });
// room2 = "room2";
// socket.on('join2', function (room2) {
// //socket.join(room2);
// console.log(room2)
// });
//socket.in(room).emit('message', 'what is going on, party people?');
// socket.in('foobar').emit('message', 'anyone in this room yet?');
socket.on('disconnect', () =>
console.log(`Disconnected: ${socket.id}`));
socket.on('join', (room) => {
console.log(`Socket ${socket.id} joining ${room}`);
socket.join(room);
});
socket.on('chat', (data) => {
const { message, room } = data;
console.log(`msg: ${message}, room: ${room}`);
io.to(room).emit('chat', message);
});
// socket.on('typing', (data) => {
// const { typing, room } = data;
// console.log(`msg: ${typing}, room: ${room}`);
// io.to(room).broadcast('typing', typing);
// });
});
}
module.exports = initializeSocketio;
Before I implemented rooms I was using List.js which did delete the input when the message was sent in the handleSentMessage function and it worked.
import React, { useEffect, useState, useRef } from "react";
import socket from "../../utils/socket/socket";
import "./listStyle.css";
var user = "User";
var store = "Store"
var storeMessage = user + " is going to " + store;
//room test
var room1 = "room1";
var room2 = "room2";
function List() {
const messageRef = useRef();
const [messages, setMessages] = useState([]);
const [typing, setTyping] = useState("")
const [room, setRoom] = useState("");
const handleSendMessage = () => {
socket.emit("chat", user + ": " + messageRef.current.value)
messageRef.current.value = ""
}
//room test
//connect room 1
const connectRoom1 = () => {
console.log("button 1 clicked")
// socket.on('join1', function (room1) {
// socket.emit('room1', room1)
// console.log("connected to room1")
// //need to do something with this
// setRoom()
// });
socket.emit('join1', "anything1")
}
//connect room 2
const connectRoom2 = () => {
// console.log("button 2 clicked")
// socket.on('join2', function () {
// socket.emit('room2', room2)
// console.log("connected to room2")
// //need to do something with this
// setRoom()
// });
socket.emit('join2', "anything2")
}
//commented out for now
// socket.on('connect', function(){
// socket.emit('room1', room1)
// });
// socket.on('connect', function(){
// socket.emit('room2', room2)
// });
//room test
socket.on('message', function (data) {
console.log('Incoming message:', data);
});
const handleTyping = () => { socket.emit("typing", user + ": is typing") }
useEffect(() => {
socket.on("chat", (data) => {
setMessages((prevMessages) => ([...prevMessages, data]));
setTyping("")
});
socket.on("typing", (data) => {
setTyping(data)
console.log(data)
});
return () => {
socket.off("chat");
}
// dependency array, put user object in array if we want the callback function to fire everytime userobject sees a change
}, [])
return (
<div>
{/* connect to room 1 */}
<button onClick={connectRoom1}> Room 1 </button>
<br></br>
{/* connect to room 2 */}
<button onClick={connectRoom2}> Room 2 </button>
<h1>{storeMessage}</h1>
<div id="list-chat">
<div id="chat-window">
<div id="output">
{messages.map((message, i) => (
<p key={i}>{message}</p>
))}
</div>
<div id="feedback">{typing}</div>
</div>
<input id="message" autoComplete="off" type="text" placeholder="message" ref={messageRef} onChange={handleTyping} />
<button id="send" onClick={handleSendMessage}>Send</button>
</div>
</div>
)
}
export default List;
any ideas on what I need to do to get the input deleted when the message is sent.
I also want to try to reimplement the user: is typing message but that's a secondary priority.
Any hints or help to get pointed in the right direction would be greatly appreciated.
Maybe you should use a state to hold your input value, and you don't to use a ref for that, try something like:
const [inputValue, setInputValue] = useState("")
const handleSendMessage = (e) => {
socket.emit("chat", user + ": " + e.target.value)
setInputValue("")
}
....
<input id="message" autoComplete="off" type="text" placeholder="message"
ref={messageRef} onChange={handleTyping} value={inputValue}/>
To the typing feature, I would suggest a debounce, to avoid unnecessary events emitted. Take a look at https://github.com/xnimorz/use-debounce, for me one of the easiest debounce solution.
I found a solution to my first part.
const [myList, setList] = useState([]);
const messageRef = useRef(null);
------
const handleSubmit = (e) => {
e.preventDefault();
setList([...myList, messageRef.current.value]);
// you can manually set the value of your input by the node ref
messageRef.current.value = "";
// Or you can reset your form element to its default values
// e.currentTarget.reset();
}
---
<form onSubmit={handleSubmit}>
</form>
I haven't tried the second part yet.