Socket.io/React Chat App - Connection established - reactjs

i have two components: where I set the username and room and then I have the component where is the send message and displays the conversation.
When I login on the component, I navigate to the component and trigger the connection with the socket:
const handleClick = (e) => {
e.preventDefault();
navigate("/chatroom", { state: { username, room } });
};
I have two issues regarding the connection with the socket:
-I´ve tried to start the connection inside an useEffect():
export default function ChatRoom() {
const [chatMessage, setChatMessage] = useState("");
const [showmsg, setShowMsg] = useState([]);
const [showObj, setShowObj] = useState([]);
const [submited, setSubmited] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
setShowMsg((oldmsg) => [...oldmsg, chatMessage]);
setChatMessage("");
setSubmited(true);
};
const { state } = useLocation(); //to get data from <Home/> component
useEffect(() => {
const socket = io("http://localhost:3000");
//Message from server
socket.on("message", (msg) => {
setShowObj((oldmsg) => [...oldmsg, msg]);
setSubmited(false);
console.log(showObj);
});
//Message to server
socket.emit("chatMessage", {
user: state.username,
text: showmsg[showmsg.length - 1],
}); //pass the last msg
}, [submited]);
ServerSide:
io.on("connection", (socket) => {
//Welcome current user
socket.emit("message", formatMessage("MiouriChat", "Welcome to the chat!"));
//Broadcast when user connects
socket.broadcast.emit(
"message",
formatMessage("MiouriChat", "A user has joined the chat")
);
//Run when clients disconects
socket.on("disconnect", () => {
io.emit("message", formatMessage("MiouriChat", "A user has left the chat"));
});
//Listen to chat message from client
socket.on("chatMessage", (msg) => {
io.emit("message", formatMessage(msg.user, msg.text));
});
});
This way, everytime I submit a msg ([submited] change), the connection is reseted for obvious reason and get the "welcome" messages everytime it reconects.
If I put the connection outside the useEffect(), the connection reseted everytyme I type a letter on the message input (becouse the state updates.)
What is the best solution for this?

For your initial "welcome" message, you want a useEffect that only runs after the first render of the page as explained in the docs - by specifying an empty dependency array. This is also a really nice place to define the "cleanup" function, where you want to send a "disconnect" message to your server (in fact the cleanup docs even use a chat API as an example!):
useEffect(() => {
const socket = io("http://localhost:3000");
//Message from server
socket.on("message", (msg) => {
setShowObj((oldmsg) => [...oldmsg, msg]);
console.log(showObj);
});
return () => {
socket.emit("disconnect"); // cleanly disconnect from server
};
}, []); // <- Empty dependency array === only runs after initial render
That solves the "welcome message" and "disconnect" problems, and you no longer need the submited state variable. I can't really help you with the rest, but again I commend the useEffect documentation to you, particularly the example which is so applicable to your use case!

Related

Socket call multiple time that first give me ui with update two time then merge with original response

//this is an function that receive real time msg that other user send.
api call to get conversation list...
const handleListMyContact = useCallback(async () => {
const data = await getConversation(authToken);
if (data) {
setListData(data.info);
} else {
console.log("loading data...");
}
}, []);
useEffect(() => {
if (focus) {
handleListMyContact();
}
}, [handleListMyContact, focus]);
const receivePersonalMessage = useCallback((data: any) => {
console.log("data from emit", data);
const contactList: any = listData;
const idxObj = contactList.findIndex((object: any) => {
return object.conversation_id === data.conversation_id;
});
console.log("object find....", idxObj);
contactList.splice(idxObj, 1);
contactList.unshift(data);
console.log("contact list", contactList[0].message);
setListData((prev: any) => [contactList[0], ...prev]);
console.log("clicked...");
handleListMyContact(); // this is my api call that code is above
}, []);
//then I set socket globally. so in useEffect i direct call that socket.on instance.
useEffect(() => {
socket.on("new_conversation_message", receivePersonalMessage);
return () => {
socket.off("new_conversation_message", receivePersonalMessage);
};
}, [socket, receivePersonalMessage]);
whenever other user send the msg at the conversation tab i can see first two response then it render with original one.
// this is log i got from receivePersonalMessage function
object find.... -1
contact list Hi
clicked...
object find.... -1
contact list Hi
clicked...
and the problem is first it shows me hi msg first time in alone then marge with group but actually I want hi msg should be direct update to the group not two times in another user screen then show in orginal group. could you please help me out? I really appreciate if you could help me. Thank you in advance

store data in firestore when Browser Tab is closed or the route is changed (react JS)

const handleDraftContracts = async () => {
console.log('/bruhhhhhhandleDraftContract');
const paragraphRef: string | any = document.getElementById('contract');
const contractDetails = {
contractName: 'House Rental',
states: {
amount: amount,
},
content: paragraphRef?.textContent,
};
await makeDraftContract(contractDetails);
};
useEffect(() => {
console.log('///////I am hreeeee');
window.addEventListener('onbeforeunload', (env) => {
handleDraftContracts();
});
return () => {
console.log('///////removing');
window.removeEventListener('onbeforeunload', handleDraftContracts);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
firestore.js
// make Draft Contracts
export async function makeDraftContract(contractDetails: object | any) {
try {
console.log("making a draft contract", contractDetails);
const draftContractRef: any = collection(db,"makeDraftContracts");
let contract = await addDoc(draftContractRef, contractDetails);
console.log("./////////makeDraftContract", contract);
} catch (error) {
console.log('////errror in contract Hanlder', error);
}
}
I want to call my handleDraftContracts method whenever user closes the tab or changes the route. I am using onbeforeunload event. The handleDraftContracts is getting called but the tab unloads before Firestore could update the collection. How can I get around this that as the user closes the tab or move to a new route, my firestore method get executed first then the tab gets unloaded ?
Try with Beacon api
https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API
as 'onbeforeunload' cannot make sure you request to server has been made and requests can slow down the browser
componentWillUnmount is like that one, cannot to make long running script.

Unable to get the socket id on the front end

I need to get each socket's id on the frontend so later I can distinguish each connection. When I print socketRef.current I can clearly see whatever is inside it and even "id" in it but only the "id" is the thing I cannot print from there (if I print "io" or "ids" it works). Why I cannot print the "id" field? Is there a way to get id properly from the backend?
const socketRef = useRef();
useEffect(() => {
socketRef.current = io.connect('http://localhost:5000')
console.log(socketRef.current.id)
}, []);
connect io and on socket connect get socket.id
const socket = io.connect('http://localhost:5000');
socket.on("connect", () => {
console.log(socket.id);
});
I believe need to wait for the connect first:
useEffect(() => {
socketRef.current = io.connect('http://localhost:5000')
socketRef.current.on('connect', ()=> {
console.log(socketRef.current.socket.sessionid);
});
}, []);

React/Socket.io - Client emits to all other clients only once and then only emits to itself after that

Running into an issue with React/Socket.io. I have two different socket emitters/listeners: one for a chat, and one for keeping track live changes to the application. I have two separate windows running localhost. The issue is when i emit a change on one window, the other window can receive that change the first time but never again (i.e. get first chat message but none that follow). After that first emit/receive, the sending client starts to receive its own emitters.
front end code:
`
socket = io("localhost:3002");
componentDidMount() {
//get id from url
const { id } = this.props.match.params;
//join specific room for project
this.socket.on("connect", () => {
this.socket.emit("room", this.projectId);
});
//listener for incoming messages
this.socket.on("RECEIVE_MESSAGE", (data) => {
this.props.addChat(this.projectId, data);
});
this.socket.on("UPDATE_PROJECT", () => {
console.log("update");
this.props.fetchProject(id);
});
}
emitTaskChange = () => {
this.socket.emit("TASK_CHANGE", { data: null });
};
onChatSubmit = (e) => {
e.preventDefault();
//create object with current user as author, message, and a timestamp
const chat = {
author: this.props.currentUser.name,
message: this.state.newChat,
createdAt: new Date().toLocaleString(),
};
//send message through socket
this.socket.emit("SEND_MESSAGE", chat);
//call action creator to add new chat
this.props.addChat(this.projectId, chat);
this.setState({ currentMessage: "" });
};
handleTaskEdit = (taskId, currentStatus) => {
const newStatus = currentStatus === "todo" ? "inprogress" : "completed";
this.props.editTask(this.projectId, taskId, newStatus);
this.emitTaskChange();
};
`
backend code:
`
const io = socket(server);
//create separate chat rooms using project id
io.on("connection", (socket) => {
socket.on("room", (room) => {
socket.join(room);
socket.in(room).on("SEND_MESSAGE", (message) => {
socket.emit("RECEIVE_MESSAGE", message);
});
socket.in(room).on("TASK_CHANGE", (data) => {
socket.emit("UPDATE_PROJECT", data);
});
});
`
found the error:
had to change the server-side code from socket.on and instead use the io object that was initialized such as io.sockets.on

How to subscribe to a new websocket session when switching chats in react

I am working on a live chat feature where users can join many different chatrooms. I'm having difficulty subscribing to the new chatroom once switching rooms. It is sending the messages to the correct session, but not receiving them since it's not re-subscripting to the new session.
useEffect(() => {
client.configure({
brokerURL: 'ws://localhost:8080/ws',
connectHeaders: sh,
onConnect: () => {
console.log('onConnect');
setIsConnected(true);
client.subscribe(`/secure/room/${convoId}`, mes => {
const json = JSON.parse(mes.body);
setMessages(prev => prev.concat(json));
});
},
onDisconnect: () => {
console.log('disconnected');
setIsConnected(false);
},
onWebSocketClose: () => {
client.unsubscribe();
}
});
client.activate();
}, [messages, convoId]);
Once a user switches to a new chatroom, to re-subscribe to the session
Try to close the connection first before reconfigure it.
like client.disconnect();

Resources