Socket.io connection is not disconnecting properly when browser tab or window is closed

Viewed 168

I am building a messenger app and storing all online users along with their socket ids in mongodB. I'm currently updating user status whenever connect or disconnect event is emitted from my React client and manually disconnecting with socket.disconnect() whenever the user logs out or when the React component is unmounted with the useEffect hook. But at times the dB is not updated with the correct user status(online or offline) and the socket ids aren't removed either, mostly when the browser window or tab is closed.

Below is the code for my mongoose queries in my Node server, on connect and disconnect events:

io.on('connection', async (socket) => {
    const userId = socket.handshake.query.userId;

    // Update status and socket id of current user
    await User.findByIdAndUpdate({_id: userId}, {
        status: 'online',
        $push: {socketId: socket.id}
    }, {new: true});

    // Emit notification to all online friends
    const currUserFriends = await User.findOne({_id: userId}, {friends: 1, username: 1}).populate('friends', 'status socketId');
    if(currUserFriends && currUserFriends.friends.length > 0) {
        const onlineFriends = currUserFriends.friends.filter(friend => friend.status === 'online');
        onlineFriends.forEach(friend => {
            friend.socketId.forEach(socket => {
                io.to(socket).emit('newOnlineFriend', {_id: userId, username: currUserFriends.username});
            });
        });
    }

    socketMain(io, socket, userId);
    console.log(`connected to worker: ${cluster.worker.id}`);
    socket.on('disconnect', async (reason) => {            
        // Check if user has more than one connections
        const foundUser = await User.findOne({_id: userId}).populate('friends', 'status socketId');
        if(foundUser && foundUser.socketId.length > 1) {
            await User.findByIdAndUpdate({_id: userId}, {$pull: {socketId: socket.id}});
        } else {
            await User.findByIdAndUpdate({_id: userId}, {
                status: 'offline', 
                $pull: {socketId: socket.id}
            });
        }

        // Emit notification to all online friends
        if(foundUser && foundUser.friends.length > 0) {
            const onlineFriends = foundUser.friends.filter(friend => friend.status === 'online');
            onlineFriends.forEach(friend => {
                friend.socketId.forEach(socket => {
                    io.to(socket).emit('newOfflineFriend', {_id: userId});
                });
            });
        }

        socket.disconnect(true);
    });
});

I also thought if it might be an issue in the dev environment since I'm running my node server with nodemon and since it reloads with every minor change, the dB update might have not occured, but I'm not really sure.

Please help me identify the issue. Thank You :)

0 Answers
Related