Reputation: 509
I am trying to code the private messaging socketio example from: https://github.com/socketio/socket.io/tree/master/examples/private-messaging in React. However, I think I am initializing the connection incorrectly because every time the page refreshes, it creates a new connection. I don't really know where to go from here. Backend is the same as the repo above and this is the Chat component.
import React, { useEffect, useRef, useState } from "react";
import User from "./User";
import MessagePanel from "./MessagePanel";
import { io } from "socket.io-client";
import socketIo from '@/utils/socket'
function useSocket(url, username) {
const [socket, setSocket] = useState(null)
useEffect(() => {
const socketIo = io(url, { auth:{username}, autoConnect: true });
setSocket(socketIo)
}, [])
return socket
}
export default function Chat(props) {
const [selectedUser, setSelectedUser] = useState(null)
const [users, setUsers] = useState([])
const API_URL='http://localhost:5000'
const socket = useSocket(API_URL, props.name)
useEffect(() => {
const handleConnect = () => {
console.log("A user has connected")
const tempUsers = [...users]
tempUsers.forEach((user) => {
if (user.self) {
user.connected = true
}
});
setUsers(tempUsers)
}
if (socket) {
const sessionID = localStorage.getItem("sessionID");
if (sessionID) {
socket.auth = {...socket.auth, sessionID };
// socket.connect();
}
socket.on("session", ({ sessionID, userID }) => {
console.log("reconnection attempted")
// attach the session ID to the next reconnection attempts
socket.auth = {...socket.auth, sessionID };
// store it in the localStorage
localStorage.setItem("sessionID", sessionID);
// save the ID of the user
socket.userID = userID;
});
socket.on("connect", handleConnect)
socket.on("disconnect", () => {
const tempUsers = [...users]
tempUsers.forEach((user) => {
if (user.self) {
user.connected = false
}
});
console.log("tempUsers after disconnect" + tempUsers)
setUsers(tempUsers)
});
socket.on("users", (usrs) => {
const tempUsers = [...users]
usrs.forEach((user) => {
for (let i = 0; i <= tempUsers.length; i++) {
const existingUser = tempUsers[i];
if (existingUser && existingUser.userID === user.userID) {
existingUser.connected = user.connected;
setUsers(tempUsers)
return;
}
}
user.self = user.userID === socket.userID;
initReactiveProperties(user);
tempUsers.push(user);
});
tempUsers.sort((a, b) => {
if (a.self) return -1;
if (b.self) return 1;
if (a.username < b.username) return -1;
return a.username > b.username ? 1 : 0;
});
setUsers(tempUsers)
// put the current user first, and sort by username
});
socket.on("user connected", (user) => {
const tempUsers = [...users]
for (let i = 0; i < tempUsers.length; i++) {
const existingUser = tempUsers[i];
if (existingUser.userID === user.userID) {
existingUser.connected = true;
setUsers(tempUsers)
return;
}
}
initReactiveProperties(user);
tempUsers.push(user)
console.log("tempUsers:" + JSON.stringify(tempUsers))
setUsers(tempUsers)
});
socket.on("user disconnected", (id) => {
const tempUsers = [...users]
for (let i = 0; i < tempUsers.length; i++) {
const tempUser = tempUsers[i];
if (tempUser.userID === id) {
tempUser.connected = false;
break;
}
}
setUsers(tempUsers)
});
socket.on("private message", ({ content, from, to }) => {
const tempUsers = [...users]
for (let i = 0; i < tempUsers.length; i++) {
const fromSelf = socket.userID === from;
if (tempUsers[i].userID === (fromSelf ? to : from)) {
tempUsers[i].messages.push({
content,
fromSelf,
});
if (tempUsers[i] !== selectedUser) {
tempUsers[i].hasNewMessages = true;
}
break;
}
}
setUsers(tempUsers)
});
}
function cleanup() {
socketIo.off("connect_error");
socketIo.off("connect");
socketIo.off("disconnect");
socketIo.off("users");
socketIo.off("user connected");
socketIo.off("user disconnected");
socketIo.off("private message");
}
return cleanup
}, [socket])
const initReactiveProperties = (user) => {
user.messages = [];
user.hasNewMessages = false;
};
function usernameInsideList(username, list) {
for (let i = 0; i < list.length; i++) {
if (list[i].username === username) {
return true;
}
}
return false;
}
function onSelectUser(user, idx) {
console.log("User was selected")
setSelectedUser(user);
// user.hasNewMessages = false
// selectedUser.current = user
if (user.hasNewMessages) {
let newArr = [...users];
newArr[idx].hasNewMessages = false
setUsers(newArr)
}
}
function onMessage(content) {
if (selectedUser) {
socket.emit("private message", {
content,
to: selectedUser.userID,
});
const tempUser = {...selectedUser};
tempUser.messages.push({
content,
fromSelf: true,
});
setSelectedUser(tempUser)
}
}
function RightPanel() {
if (selectedUser) {
return (
<MessagePanel
user={selectedUser}
onMessage={onMessage}
/>);
} else {
return (
"Hello"
)
}
}
return (
<div>
<div className="left-panel">
{users.map((user, id) => {
return <User key={id} handleClick={onSelectUser} user={user}></User>;
})}
</div>
<div className="right-panel">
<RightPanel/>
</div>
</div>
);
}
Edit: I fixed the original issue by making the following edit. However, the code certainly isn't working as expected.
import React, { useEffect, useRef, useState } from "react";
import User from "./User";
import MessagePanel from "./MessagePanel";
import { io } from "socket.io-client";
export default function Chat(props) {
const [selectedUser, setSelectedUser] = useState(null)
const username = props.name;
const [users, setUsers] = useState([])
const API_URL='http://localhost:5000'
const socket = useRef();
useEffect(() => {
socket.current = io(API_URL, { auth:{username}, autoConnect: true });
const handleConnect = () => {
const tempUsers = [...users]
tempUsers.forEach((user) => {
if (user.self) {
user.connected = true
}
});
setUsers(tempUsers)
}
if (socket.current) {
const sessionID = localStorage.getItem("sessionID");
if (sessionID) {
socket.current.auth = {...socket.current.auth, sessionID };
// socket.connect();
}
socket.current.on("session", ({ sessionID, userID }) => {
console.log("reconnection attempted")
// attach the session ID to the next reconnection attempts
socket.current.auth = {...socket.current.auth, sessionID };
// store it in the localStorage
localStorage.setItem("sessionID", sessionID);
// save the ID of the user
socket.current.userID = userID;
});
socket.current.on("connect", handleConnect)
socket.current.on("disconnect", () => {
const tempUsers = [...users]
tempUsers.forEach((user) => {
if (user.self) {
user.connected = false
}
});
console.log("tempUsers after disconnect" + tempUsers)
setUsers(tempUsers)
});
socket.current.on("users", (usrs) => {
const tempUsers = [...users]
usrs.forEach((user) => {
for (let i = 0; i <= tempUsers.length; i++) {
const existingUser = tempUsers[i];
if (existingUser && existingUser.userID === user.userID) {
existingUser.connected = user.connected;
setUsers(tempUsers)
return;
}
}
user.self = user.userID === socket.userID;
initReactiveProperties(user);
tempUsers.push(user);
});
setUsers(tempUsers)
// put the current user first, and sort by username
});
socket.current.on("user connected", (user) => {
const tempUsers = [...users]
for (let i = 0; i < tempUsers.length; i++) {
const existingUser = tempUsers[i];
if (existingUser.userID === user.userID) {
existingUser.connected = true;
setUsers(tempUsers)
return;
}
}
initReactiveProperties(user);
tempUsers.push(user)
setUsers(tempUsers)
});
socket.current.on("user disconnected", (id) => {
const tempUsers = [...users]
for (let i = 0; i < tempUsers.length; i++) {
const tempUser = tempUsers[i];
if (tempUser.userID === id) {
tempUser.connected = false;
break;
}
}
setUsers(tempUsers)
});
socket.current.on("private message", ({ content, from, to }) => {
console.log("private message")
const tempUsers = [...users]
for (let i = 0; i < tempUsers.length; i++) {
const fromSelf = socket.userID === from;
if (tempUsers[i].userID === (fromSelf ? to : from)) {
tempUsers[i].messages.push({
content,
fromSelf,
});
console.log("tempUser and selectedUser" + tempUser[i] + selectedUser)
if (tempUsers[i] !== selectedUser) {
tempUsers[i].hasNewMessages = true;
}
break;
}
}
setUsers(tempUsers)
});
}
function cleanup() {
socket.current.off("connect_error");
socket.current.off("connect");
socket.current.off("disconnect");
socket.current.off("users");
socket.current.off("user connected");
socket.current.off("user disconnected");
socket.current.off("private message");
}
return cleanup
}, [])
//Use this to decrease DRY
// const updateState = (state, setState, ...args) => {
// console.log(args)
// let newArr = [...state];
// newArr[idx].connected = true
// setState(newArr)
// }
const initReactiveProperties = (user) => {
user.messages = [];
user.hasNewMessages = false;
};
function usernameInsideList(username, list) {
for (let i = 0; i < list.length; i++) {
if (list[i].username === username) {
return true;
}
}
return false;
}
function onSelectUser(user, idx) {
setSelectedUser(user);
// selectedUser.current = user
if (user.hasNewMessages) {
let newArr = [...users];
newArr[idx].hasNewMessages = false
setUsers(newArr)
}
}
function onMessage(content) {
if (selectedUser) {
socket.current.emit("private message", {
content,
to: selectedUser.userID,
});
const tempUser = {...selectedUser};
tempUser.messages.push({
content,
fromSelf: true,
});
setSelectedUser(tempUser)
}
}
function RightPanel() {
if (selectedUser) {
return (
<MessagePanel
user={selectedUser}
onMessage={onMessage}
/>);
} else {
return (
"Hello"
)
}
}
return (
<div>
<div className="left-panel">
{users.map((user, id) => {
return <User key={id} idx={id} handleClick={onSelectUser} user={user}></User>;
})}
</div>
<div className="right-panel">
<RightPanel/>
</div>
</div>
);
}
Here is the vue component I am trying to recreate.
<template>
<div>
<div class="left-panel">
<user
v-for="user in users"
:key="user.userID"
:user="user"
:selected="selectedUser === user"
@select="onSelectUser(user)"
/>
</div>
<message-panel
v-if="selectedUser"
:user="selectedUser"
@input="onMessage"
class="right-panel"
/>
</div>
</template>
<script>
import socket from "../socket";
import User from "./User";
import MessagePanel from "./MessagePanel";
export default {
name: "Chat",
components: { User, MessagePanel },
data() {
return {
selectedUser: null,
users: [],
};
},
methods: {
onMessage(content) {
if (this.selectedUser) {
socket.emit("private message", {
content,
to: this.selectedUser.userID,
});
this.selectedUser.messages.push({
content,
fromSelf: true,
});
}
},
onSelectUser(user) {
this.selectedUser = user;
user.hasNewMessages = false;
},
},
created() {
socket.on("connect", () => {
this.users.forEach((user) => {
if (user.self) {
user.connected = true;
}
});
});
socket.on("disconnect", () => {
this.users.forEach((user) => {
if (user.self) {
user.connected = false;
}
});
});
const initReactiveProperties = (user) => {
user.messages = [];
user.hasNewMessages = false;
};
socket.on("users", (users) => {
users.forEach((user) => {
for (let i = 0; i < this.users.length; i++) {
const existingUser = this.users[i];
if (existingUser.userID === user.userID) {
existingUser.connected = user.connected;
return;
}
}
user.self = user.userID === socket.userID;
initReactiveProperties(user);
this.users.push(user);
});
// put the current user first, and sort by username
this.users.sort((a, b) => {
if (a.self) return -1;
if (b.self) return 1;
if (a.username < b.username) return -1;
return a.username > b.username ? 1 : 0;
});
});
socket.on("user connected", (user) => {
for (let i = 0; i < this.users.length; i++) {
const existingUser = this.users[i];
if (existingUser.userID === user.userID) {
existingUser.connected = true;
return;
}
}
initReactiveProperties(user);
this.users.push(user);
});
socket.on("user disconnected", (id) => {
for (let i = 0; i < this.users.length; i++) {
const user = this.users[i];
if (user.userID === id) {
user.connected = false;
break;
}
}
});
socket.on("private message", ({ content, from, to }) => {
for (let i = 0; i < this.users.length; i++) {
const user = this.users[i];
const fromSelf = socket.userID === from;
if (user.userID === (fromSelf ? to : from)) {
user.messages.push({
content,
fromSelf,
});
if (user !== this.selectedUser) {
user.hasNewMessages = true;
}
break;
}
}
});
},
destroyed() {
// socket.off("connect");
// socket.off("disconnect");
// socket.off("users");
// socket.off("user connected");
// socket.off("user disconnected");
// socket.off("private message");
},
};
</script>
<style scoped>
.left-panel {
position: fixed;
left: 0;
top: 0;
bottom: 0;
width: 260px;
overflow-x: hidden;
background-color: #3f0e40;
color: white;
}
.right-panel {
margin-left: 260px;
}
</style>
Upvotes: 0
Views: 964
Reputation: 134
Socket connections are disconnected on page refresh and that is the expected behavior across browsers.
Upvotes: 1