English 中文(简体)
correct socket.io implementation
原标题:

I m trying out socket io for my project to show online friends, and I was wondering (actually, it looks kinda strange to me) that whenever I try to rerender the page (it doesn t matter if a user changes his profile info or send a friend req) the useEffect which is in charge of initializing the socket will disconnect and reconnect. Well... I agree that if the page is being refreshed or closed, the socket should be closed, but not whenever a user sends a friend request or changes his profile info... I just started to include the socket and the functionality is not full but so far at the beginning, having these various reconnections and creation of Sockets is strange. plusssss, my main concern shows itself on rerender; the socket will be disconnected (which is normal) and won t connect back (which is abnormal), but i only want this behavior on actual refreshes or logouts :( again, I will try to be as clear as possible, I have my case open on my screen for further explanation on your questions asap :)

File (Client): socket.js:

import { io } from  socket.io-client ;

export const initializeSocket = (token, userInfoQuery, setOnlineFriends, setSocket) => {
  if (token && userInfoQuery) {
    const socket = io(process.env.REACT_APP_API_URL, { auth: { token } });

    const friendIds = userInfoQuery.friends
    socket.emit("userFriends", friendIds);

    socket.on("onlineUsers", (users) => {
      setOnlineFriends(users);
    });

    socket.on("addFriend", (friend) => {
      console.log("Received friend request:", friend);
    });

    socket.on("removeFriend", (friend) => {
      console.log("Received friend request:", friend);
    });

    setSocket(socket);
    return socket;
  }
  return null;
};

File (Client): App.js:

export default function App() {
  const { token, logout, login } = useContext(AuthContext);
  const [onlineFriends, setOnlineFriends] = useState([]);
  const [socket, setSocket] = useState(null);

  const userInfoQuery = useQuery("userInfo", async () => {
    const response = await axios.get(
      `${process.env.REACT_APP_API_URL}/api/user`,
      {
        headers: {
          Authorization: "Bearer " + token,
        },
      }
    );
    return response.data;
  });
  const userBooksQuery = useQuery("userBooks", async () => {
    const response = await axios.get(
      `${process.env.REACT_APP_API_URL}/api/user/books`,
      {
        headers: {
          Authorization: "Bearer " + token,
        },
      }
    );
    return response.data;
  });

  useEffect(() => {
    if (socket) {
      socket.connect();
    }
    if (token && userInfoQuery.data && !socket) {
      initializeSocket(token, userInfoQuery.data, setOnlineFriends, setSocket);
    }

    return () => {
      if (socket) {
        socket.disconnect();
      }
    };
  }, [token, userInfoQuery.data, socket]);

return ( ...
)

as you can see, I initaiated the socket and all its functionality on one useEffect and those dep array. not having the userInfoQuery as dep would not rerender the page since online friends are coming from the socket.

File (Server): socketController.js:

const knexConfig = require("../knexfile");
const { knex } = require("knex");
const db = knex(knexConfig);
const io = require("socket.io");
const jwt = require("jsonwebtoken");
require("dotenv").config();

module.exports.socketController = (io) => {
  const onlineFriends = new Map();

  io.on("connection", async (socket) => {
    console.log(`A user connected ${socket.id}`);
    try {
      const token = socket.handshake.auth.token;
      const decoded = jwt.verify(token, process.env.JWT_SIGN_KEY);
      const userId = decoded.user_id;

      await db("user").where("user_id", userId).update({ is_online: 1 });
      const user = await db("user").where("user_id", userId).first();

      onlineFriends.set(userId, user);

      socket.join(userId);

      const onlineFriendList = Array.from(onlineFriends.values());
      socket.emit("onlineUsers", onlineFriendList);

      socket.on("userFriends", async (friendsList) => {
        friendsList.forEach((friend) => {
          socket.join(friend.friend);
        });

        const onlineUsers = await db("user")
          .whereIn(
            "user_id",
            friendsList.map((friend) => friend.friend)
          )
          .andWhere("is_online", 1);

        onlineUsers.forEach((user) => onlineFriends.set(user.user_id, user));
        const updatedOnlineFriendList = Array.from(onlineFriends.values());

        socket.broadcast.emit("onlineUsers", updatedOnlineFriendList);
      });
      socket.on("addFriend", async (friendId) => {
        const recipientUser = onlineFriends.get(friendId); 
        if (recipientUser) {
          const recipientSocket = io.sockets.sockets.get(recipientUser.socket_id); 
          if (recipientSocket) {
            recipientSocket.emit("addFriend", userId);
          }
        }
      });
      socket.on("removeFriend", async (friendId) => {
        const recipientUser = onlineFriends.get(friendId);
        if (recipientUser) {
          const recipientSocket = io.sockets.sockets.get(recipientUser.socket_id); 
          if (recipientSocket) {
            recipientSocket.emit("removeFriend", userId);
          }
        }
      });
      socket.on("disconnect", async () => {
        await db("user").where("user_id", userId).update({ is_online: 0 });

        onlineFriends.delete(userId);
        const updatedOnlineFriendList = Array.from(onlineFriends.values());

        socket.broadcast.emit("onlineUsers", updatedOnlineFriendList);

        console.log(`${socket.id} disconnected`);
      });

    } catch (error) {
      console.error("Authentication error:", error.message);
      socket.disconnect();
    }
  });
};

I feel the server-side is pretty much fine in terms of sending back the online friend users. again if the server-side had some problem or you had suggestions on it, feel free to let me know...

each user object if like this :

{
    avatar_image: "https://i.pravatar.cc/150?img=13"
    ​​email: "ross001@gmail.com"
    ​​favorite_genre: "comedy"
    ​​first_name: "ross"
    ​​friends: Array [ {…} ]
    ​goal_set: 9
    last_name: "test"​​
    username: "rosss001"
}

thanks for coming this far... this problem stoped me to move on my project for days, would appreciate if you could help me out here :)

should you have any suggestion, let me know

again, I have my case open for answering questions

Although not being efficient in useEffect, I tried to do socket.connect() to attempt to reconnect to the socket, but it seems not to work. Also, when a user changes his profile info, I tried to in my component refetch the data to trigger the useEffect by : queryClient.refetchQueries("userInfo"); . again it seems it rerenders and disconnects the whole socket and causes the problem altogether. on the other hand, I need to rerender the page to inform the user and the changes made to his account! the socket.connect() at the beginning of useEffect was my ultimate try...

问题回答

暂无回答




相关问题
selected text in iframe

How to get a selected text inside a iframe. I my page i m having a iframe which is editable true. So how can i get the selected text in that iframe.

How to fire event handlers on the link using javascript

I would like to click a link in my page using javascript. I would like to Fire event handlers on the link without navigating. How can this be done? This has to work both in firefox and Internet ...

How to Add script codes before the </body> tag ASP.NET

Heres the problem, In Masterpage, the google analytics code were pasted before the end of body tag. In ASPX page, I need to generate a script (google addItem tracker) using codebehind ClientScript ...

Clipboard access using Javascript - sans Flash?

Is there a reliable way to access the client machine s clipboard using Javascript? I continue to run into permissions issues when attempting to do this. How does Google Docs do this? Do they use ...

javascript debugging question

I have a large javascript which I didn t write but I need to use it and I m slowely going trough it trying to figure out what does it do and how, I m using alert to print out what it does but now I ...

Parsing date like twitter

I ve made a little forum and I want parse the date on newest posts like twitter, you know "posted 40 minutes ago ","posted 1 hour ago"... What s the best way ? Thanx.

热门标签