Clearing Message Input in a Socket React App when using Rooms - javascript

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.

Related

Why socket.io drops my entire server when emitting 2 events at the same time?

I'm building an e-commerce with chat. I have 2 react apps, a customer's app, and a owner's/admin's app. I can send from customer to admin in real time messages in chat/:customer component, but I want at the same time to emit an event to admin's navbar to update the inbox messages, so when a message is sent to be notified real time. Once I created this, and send a message from customer, on page refresh the entire node js server drops. Why does this happen? Bellow you can see my code:
Owner/admin NavBar(This drops my server):
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome"
import { faAngleDown } from "#fortawesome/free-solid-svg-icons"
import "../css/navBar.css"
import { useEffect, useState } from "react"
import { Link } from 'react-router-dom';
import { socket } from "./privateChat";
export const NavBar = () => {
/*I added useEffect, because otherwise the function catalogueChoices()
works only on second click!*/
useEffect(() => {
let addProduct = document.getElementById("addProduct")
let preview = document.getElementById("preview")
addProduct.style.display = "none"
preview.style.display = "none"
socket.on('new message', (data) => console.log(data))
return () => {
socket.off('new message', (data) => console.log(data))
}
}, [])
//The function that shows and hide the cataloque submenu on nav-bar.
const catalogueChoices = () => {
let addProduct = document.getElementById("addProduct")
let preview = document.getElementById("preview")
let catalogue = document.getElementById("catalogue")
let display = preview.style.display
if(display === "none"){
addProduct.style.display = "initial"
preview.style.display = "initial"
catalogue.style.height = "150px"
} else {
addProduct.style.display = "none"
preview.style.display = "none"
catalogue.style.height = "70px"
}
}
return( <div className="navBar">
<ul>
<li>
Dashboard
</li>
<li id="catalogue">
<p>Products
<span>
<button onClick={ catalogueChoices } className="angleDown" >
< FontAwesomeIcon icon={ faAngleDown } />
</button>
</span>
</p>
<Link id="addProduct" className="products" to="add-product">Add Product</Link>
<Link id="preview" className="products" to="preview">Preview</Link>
</li>
<li>
Orders
</li>
<li>
Customers
</li>
<li >
{ newMessage } </span>
</li>
</ul>
</div>
)
}
Owner/admin private chat component(works fine without navbar socket)
import "../css/chat.css"
import io from 'socket.io-client';
import {useParams} from 'react-router-dom';
import { useEffect, useState } from "react";
import { showMessage } from "../functions/chat";
import axios from "axios"
//Initializing socket.io and url's parameter name object.
export const socket = io(`http://localhost:5000`)
/*Below component is dynamically generated in order to chat with
a specific customer that sent us a message*/
export const PrivateChat = () => {
//The state needed.
const [messagesHistory, setMessagesHistory ] = useState([])
const params = useParams();
useEffect(() => {
//Holding customer's name from url in a variable.
let customer = params.customer
//Fetching all the old messages to be displayed.
axios.get('http://localhost:5000/chat-messages')
.then( res => {
let messages = res.data.filter(item => item.Customer === customer)
if(messagesHistory.length !== messages.length) setMessagesHistory(messages)
})
//Handling the socket.io event that will send us a message from admin and displaying it.
socket.on('customer '+ params.customer, (data)=> { let sender = data.sender === 'admin' ? 'me' : data.sender
let message = data.message
if(data.sender !== 'admin') showMessage(sender, message)
})
return () => {
socket.off('customer '+ params.customer, (data)=> { let sender = data.sender === 'admin' ? 'me' : data.sender
let message = data.message
if(data.sender !== 'admin') showMessage(sender, message)
})
}
}, [])
/*The function to send a message to a customer in real time,
saving it in the database and displaying it.*/
const sendMessage = (e) => {
e.preventDefault()
let username = params.customer
let message = document.getElementById("input").value
let data = { username: username,
message: message,
sender: 'admin'
}
socket.emit('chat message', data)
axios.post('http://localhost:5000/chat-messages', data )
showMessage("me", message)
}
return(<div className="chat">
<ul id="messages">
{ messagesHistory.map( item => <li>{(item.Sender === "admin" ? "me" : item.Sender) + ": " + item.Message}</li>) }
</ul>
<form id="form" action="" onSubmit={sendMessage}>
<input id="input" /><button>Send</button>
</form>
</div>)
}
customer's app:
import "../css/chat.css"
import io from 'socket.io-client';
import { useEffect, useState } from "react"
import { showMessage } from "../functions/chat";
import {useParams} from 'react-router-dom';
import axios from 'axios'
//The dynamically created component we are redirected to when we enter our username to chat with admin.
export const Chat = () => {
//The state needed
const [messagesHistory, setMessagesHistory ] = useState([])
//Initializing socket.io and url's parameter name object.
const socket = io(`http://localhost:5000`);
const params = useParams()
useEffect( () => {
//Holding customer's name from url in a variable.
let customer = params.username
//Fetching all the old messages to be displayed.
axios.get('http://localhost:5000/chat-messages')
.then(res => {
let messages = res.data.filter(item => item.Customer === customer)
if(messagesHistory.length !== messages.length) setMessagesHistory(messages)
})
//Handling the socket.io event that will send us a message from a specific customer and displaying it.
socket.on('customer '+ customer, (data) => { let sender = data.sender === customer? 'me' : 'admin'
let message = data.message
if(data.sender === 'admin') showMessage(sender, message)
console.log(0)
})
}, [])
/*The function to send a message to the admin in real time,
saving it in the database and displaying it.*/
const sendMessage = (e) => {
e.preventDefault()
let username = params.username
let sender = params.username
let message = document.getElementById("input").value
let data = { sender: sender,
message: message,
username: username
}
socket.emit('chat message', data)
axios.post('http://localhost:5000/chat-messages', data )
showMessage("me", message)
}
return(<div className="chat">
<ul id="messages">
{ messagesHistory.map( item => <li>{(item.Sender === "admin"? "admin" : "me") + ": " + item.Message}</li>) }
</ul>
<form id="form" action="" onSubmit={ sendMessage } >
<input id="input" /><button>Send</button>
</form>
</div>)
}
The common server for socket.io and shared Apis:
const express = require("express")
const app = express()
//Creating a new server for socket.io with express
const server = require('http').createServer(app)
//The socket.io server instance attached to an instance of http.Server.
const io = require('socket.io')(server, {
cors: {
origin: "*",
methods: ["GET", "POST"],
}
})
const port = process.env.PORT || 5000
const cors = require("cors")
const db = require('./database/db')
//The middlewares needed to exchange data with frontend.
app.use(cors())
app.use(express.urlencoded({extended: true}));
app.use(express.json())
//db.query("DELETE FROM chat_messages")
//------> Socket.io configurations. <------
io.on('connection', (socket) => {
/*When server receives a message from a customer or admin, we dynamically create an event
depending on customer's name, and emit it with the message to both parties. */
socket.on('chat message', (data) => {
let name = data.username
let sender = data.sender
let message = data.message
io.emit('customer ' + name, { message: message,
sender: sender})
io.emit('new message', 1)
})
})
//Importing routes
const productsRoute = require("./routes/products")
const chatRoute = require("./routes/chat")
app.use('/products', productsRoute)
app.use('/chat-messages', chatRoute)
server.listen(port, () => console.log(`App is listening on port ${port}`))
I found the solution alone. When i emit message and at the same time axios.post to save it, crushes the connection with the database and doesnt fetch anything. I had to save it from the server while grabbed from server side socket.io. Why there is anywhere this detail? Am I Columbus who found socket.io message saving?!!

Trying to figure out how to use socket.io the correct way in a useEffect that is using an axios get request to fetch messages

So far i'm stuck on my useEffect that fetches all the current messages and renders the state accordingly. as of right now it doesn't render the new state until page is refreshed.
const Home = ({ user, logout }) => {
const history = useHistory();
const socket = useContext(SocketContext);
const [conversations, setConversations] = useState([]);
const [activeConversation, setActiveConversation] = useState(null);
const classes = useStyles();
const [isLoggedIn, setIsLoggedIn] = useState(false);
const addSearchedUsers = (users) => {
const currentUsers = {};
// make table of current users so we can lookup faster
conversations.forEach((convo) => {
currentUsers[convo.otherUser.id] = true;
});
const newState = [...conversations];
users.forEach((user) => {
// only create a fake convo if we don't already have a convo with this user
if (!currentUsers[user.id]) {
let fakeConvo = { otherUser: user, messages: [] };
newState.push(fakeConvo);
}
});
setConversations(newState);
};
const clearSearchedUsers = () => {
setConversations((prev) => prev.filter((convo) => convo.id));
};
const saveMessage = async (body) => {
const { data } = await axios.post("/api/messages", body);
return data;
};
const sendMessage = (data, body) => {
socket.emit("new-message", {
message: data.message,
recipientId: body.recipientId,
sender: data.sender,
});
};
const postMessage = async (body) => {
try {
const data = await saveMessage(body);
if (!body.conversationId) {
addNewConvo(body.recipientId, data.message);
} else {
addMessageToConversation(data);
}
sendMessage(data, body);
} catch (error) {
console.error(error);
}
};
const addNewConvo = useCallback(
(recipientId, message) => {
conversations.forEach((convo) => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message);
convo.latestMessageText = message.text;
convo.id = message.conversationId;
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
const addMessageToConversation = useCallback(
(data) => {
// if sender isn't null, that means the message needs to be put in a brand new convo
const { message, sender = null } = data;
if (sender !== null) {
const newConvo = {
id: message.conversationId,
otherUser: sender,
messages: [message],
};
newConvo.latestMessageText = message.text;
setConversations((prev) => [newConvo, ...prev]);
}
conversations.forEach((convo) => {
console.log('hi', message.conversationId)
if (convo.id === message.conversationId) {
const convoCopy = { ...convo }
convoCopy.messages.push(message);
convoCopy.latestMessageText = message.text;
console.log('convo', convoCopy)
} else {
return convo
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
const setActiveChat = useCallback((username) => {
setActiveConversation(username);
}, []);
const addOnlineUser = useCallback((id) => {
setConversations((prev) =>
prev.map((convo) => {
if (convo.otherUser.id === id) {
const convoCopy = { ...convo };
convoCopy.otherUser = { ...convoCopy.otherUser, online: true };
return convoCopy;
} else {
return convo;
}
}),
);
}, []);
const removeOfflineUser = useCallback((id) => {
setConversations((prev) =>
prev.map((convo) => {
if (convo.otherUser.id === id) {
const convoCopy = { ...convo };
convoCopy.otherUser = { ...convoCopy.otherUser, online: false };
return convoCopy;
} else {
return convo;
}
}),
);
}, []);
// Lifecycle
useEffect(() => {
// Socket init
socket.on("add-online-user", addOnlineUser);
socket.on("remove-offline-user", removeOfflineUser);
socket.on("new-message", addMessageToConversation);
return () => {
// before the component is destroyed
// unbind all event handlers used in this component
socket.off("add-online-user", addOnlineUser);
socket.off("remove-offline-user", removeOfflineUser);
socket.off("new-message", addMessageToConversation);
};
}, [addMessageToConversation, addOnlineUser, removeOfflineUser, socket]);
useEffect(() => {
// when fetching, prevent redirect
if (user?.isFetching) return;
if (user && user.id) {
setIsLoggedIn(true);
} else {
// If we were previously logged in, redirect to login instead of register
if (isLoggedIn) history.push("/login");
else history.push("/register");
}
}, [user, history, isLoggedIn]);
useEffect(() => {
const fetchConversations = async () => {
try {
const { data } = await axios.get("/api/conversations");
setConversations(data);
} catch (error) {
console.error(error);
}
};
if (!user.isFetching) {
fetchConversations();
}
}, [user]);
const handleLogout = async () => {
if (user && user.id) {
await logout(user.id);
}
};
return (
<>
<Button onClick={handleLogout}>Logout</Button>
<Grid container component="main" className={classes.root}>
<CssBaseline />
<SidebarContainer
conversations={conversations}
user={user}
clearSearchedUsers={clearSearchedUsers}
addSearchedUsers={addSearchedUsers}
setActiveChat={setActiveChat}
/>
<ActiveChat
activeConversation={activeConversation}
conversations={conversations}
user={user}
postMessage={postMessage}
/>
</Grid>
</>
);
};
this is the main part im working on, the project had starter code when i began and was told not to touch the backend so i know its something wrong with the front end code. i feel like im missing something important for the socket.io
import { io } from 'socket.io-client';
import React from 'react';
export const socket = io(window.location.origin);
socket.on('connect', () => {
console.log('connected to server');
});
export const SocketContext = React.createContext();
this is how i have the socket.io setup, if anyone could point me in the right direction that would be cool. I have been reading up on socket.io as much as I can but am still pretty lost on it.
Based on the assumption the backend is working properly...
const addNewConvo = useCallback(
(recipientId, message) => {
conversations.forEach((convo) => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message);
convo.latestMessageText = message.text;
convo.id = message.conversationId;
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
setConversations(conversations);
This is an incorrect way to set a state using the state's variable, and such it wont do anything. Likely why your code wont change until refresh.
Suggested fix:
const addNewConvo = useCallback(
(recipientId, message) => {
setConversations(previousState => previousState.map(convo => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message)
convo.latestMessageText = message.text;
convo.id = message.conversationId;
return convo
}
return convo
}))
},
[setConversations, conversations],
);
note: even above could be done more efficiently since I made a deep copy of messages

TypeError: Cannot read property 'room' of undefined at Socket.<anonymous> (C:\CHAT\server\index.js:22:21)

I have been getting this error and I tried all I can To solve it but none. I feel the User is not being fetched from Users.js file. here is Index.js file
const socketio = require("socket.io");
const router = require("./router");
const http = require("http");
const cors = require("cors");
const port = process.env.PORT || 5000;
const app = express();
const server = http.createServer(app);
const { addUser, removeUser, getUser, getUserInRoom } = require("./users");
const io = socketio(server);
app.use(cors());
// app.use(router);
io.on("connection", (socket) => {
console.log("We have a new connection!");
socket.on("join", ({ name, room }, callback) => {
const { user } = addUser({ id: socket.id, name, room });
console.log(user);
// if (error) return callback(error);
socket.emit("message", {
user: "admin",
text: `${user.name}, welcome to the room ${user.room}.`,
});
socket.broadcast
.to(user.room)
.emit("message", { user: "admin", text: `${user.name} has joined` });
socket.join(user.room);
callback();
});
socket.on("sendMessage", (message, callback) => {
const user = getUser(socket.id);
console.log(user);
io.to(user.room).emit("message", { user: user.name, text: message });
callback();
});
socket.on("disconnect", () => {
console.log("User had just left!");
});
});
server.listen(port, () => console.log(`Server has started on port ${port}`));
Here is My Users.js file
const users = [];
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase();
room = room.trim().toLowerCase();
const existingUser = users.find((user) => {
user.room === room && user.name === name;
});
if (existingUser) {
return { error: "Username is taken" };
}
const user = { id, name, room };
users.push(user);
return { user };
};
const removeUser = (id) => {
const index = users.findIndex((user) => {
user.id === id;
});
if (index !== -1) {
return users.splice(index, 1)[0];
}
};
const getUser = (id) => {
users.find((user) => user.id === id);
};
const getUserInRoom = (room) => {
users.filter((user) => user.room === room);
};
module.exports = { addUser, removeUser, getUser, getUserInRoom };
I have searched through for any possible misplaced variable but i found none.
And Lastly, My client side
import "./Chat.css";
import queryString from "query-string";
import io from "socket.io-client";
import { InfoBar } from "../InfoBar/InfoBar";
import { Input } from "../Input/Input";
import { Messages } from "../Messages/Messages";
let socket;
export const Chat = ({ location }) => {
const [name, setName] = useState("");
const [message, setMessage] = useState("");
const [messages, setMessages] = useState([]);
const [room, setRoom] = useState("");
const ENDPOINT = "localhost:5000";
// ("ws://localhost:5000", { transports: ["websocket", "polling"] });
useEffect(() => {
socket = io(ENDPOINT);
const { name, room } = queryString.parse(location.search);
// console.log(name, room);
setName(name);
setRoom(room);
// console.log(socket);
socket.emit("join", { name, room }, () => {});
return () => {
socket.emit("disconnect");
socket.off();
};
}, [ENDPOINT, location.search]);
//UseEffect for the messages
useEffect(() => {
socket.on("message", (message) => {
setMessages(messages=>[...messages, message]);
});
}, []);
const sendMessage = (event) => {
event.preventDefault();
if (message) {
socket.emit("sendMessage", message, () => setMessage(""));
}
};
console.log(message, messages);
return (
<div className="outerContainer">
<div className="container">
<InfoBar room={room} />
<Input
setMessage={setMessage}
message={message}
sendMessage={sendMessage}
/>
<Messages messages={messages} />
{/* <input
value={message}
onChange={(event) => setMessage(event.target.value)}
onKeyPress={(event) =>
event.key === "Enter" ? sendMessage(event) : null
}
/> */}
</div>
</div>
);
};
I will appreciate every available help
You don't have a variable named user, thus user is undefined and you can't get a property of an undefined object.
const { user } = addUser({ id: socket.id, name, room });
does not create a user variable, but an unnamed object, which has a property named user.
I don't know, why your addUser function does not just return the created user object but encapsulates it in an object. Maybe you could just do
const addUser = ({ id, name, room }) => {
...
const user = { id, name, room };
users.push(user);
return user;
}
and then
const user = addUser(....)
If that is not possible and the return type of addUser can't be changed, you could do the following
const user = addUser(...).user;
socket.emit("message", {
user: "admin",
text: `${user.name}, welcome to the room ${user.room}.`,
});
You do not really need the {} around the return
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase();
room = room.trim().toLowerCase();
const existingUser = users.find((user) => {
user.room === room && user.name === name;
});
if (existingUser) {
return { error: "Username is taken" };
}
const user = { id, name, room };
users.push(user);
return { user };
};
Instead do it like this:
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase();
room = room.trim().toLowerCase();
const existingUser = users.find((user) => {
user.room === room && user.name === name;
});
if (existingUser) {
return { error: "Username is taken" };
}
const user = { id, name, room };
users.push(user);
return user;
};

send socket request from one component to different component

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]);

audio is shown disable in twilio video chat

I am trying to create a video chat with twilio. I could turn the webcam and run the video, however i could not make the audio work. When i select the control, i get to enlarge the video and picture to picture mode but not control the audio.
This is how seen
Here is the code
function App() {
let localMediaRef = React.useRef(null);;
const [data, setIdentity] = React.useState({
identity: null,
token: null
});
const [room, setRoom] = React.useState({
activeRoom: null,
localMediaAvailable: null,
hasJoinedRoom: null
});
async function fetchToken() {
try {
const response = await fetch("/token");
const jsonResponse = await response.json();
const { identity, token } = jsonResponse;
setIdentity({
identity,
token
});
} catch (e) {
console.error("e", e);
}
}
React.useEffect(() => {
fetchToken();
}, []);
const attachTracks = (tracks, container) => {
tracks.forEach(track => {
container.appendChild(track.attach());
});
};
// Attaches a track to a specified DOM container
const attachParticipantTracks = (participant, container) => {
const tracks = Array.from(participant.tracks.values());
attachTracks(tracks, container);
};
const roomJoined = room => {
// Called when a participant joins a room
console.log("Joined as '" + data.identity + "'");
setRoom({
activeRoom: room,
localMediaAvailable: true,
hasJoinedRoom: true
});
// Attach LocalParticipant's Tracks, if not already attached.
const previewContainer = localMediaRef.current;
if (!previewContainer.querySelector("video")) {
attachParticipantTracks(room.localParticipant, previewContainer);
}
};
const joinRoom = () => {
let connectOptions = {
name: "Interview Testing"
};
let settings = {
audio: true
}
console.log('data', data, data.token)
Video.connect(
data.token,
connectOptions,
settings
).then(roomJoined, error => {
alert("Could not connect to Twilio: " + error.message);
});
};
return (
<div className="App">
<FeatureGrid>
<span onClick={joinRoom}>Webcam</span>
</FeatureGrid>
<PanelGrid>
{room.localMediaAvailable ? (
<VideoPanels>
<VideoPanel ref={localMediaRef} />
</VideoPanels>
) : (
""
)}
</PanelGrid>
</div>
);
}
export default App;
How do i enable audio too? Also the settings of video is shown only after right click. can't we show this by default?
UPDATE
its a LocalAudioTrack
this is remoteaudiotrack

Categories

Resources