zoran jeremic
zoran jeremic

Reputation: 2138

Socket.io disconnect on React client is not triggered on page refresh

I'm having React.js + Node.js application where I'm using Socket.io to notify UI side that some changes have happened in the backend and it should pull these changes. I'm using client ID as identifier in socket, so I don't have to maintain map of users on the server side.

When user login I'm establishing connection and this workflow is working fine. The problem is happening when user refreshes the page. In the Chrome browser on client socket side 'disconnect' doesn't get trigger and socket doesn't get reconnected. In the FF disconnect get triggered on the first refresh, but joinsuccess is not retrieved on the client side and notifications are not received on the client. On the second and every later refresh, disconnect is not triggered.

My client code:

const io = require('socket.io-client');

export default function () {
    const socket = io.connect(process.env.REACT_APP_BACKEND_URL);

    function registerDisconnectHandler(onDisconnect) {
        socket.on('disconnect', () => {
            console.log('Disconnected');
            onDisconnect();
        });
    }
    function registerJoinHandler(onJoinSuccess) {
        socket.on('joinsuccess', (msg) => {
            console.log('join success');
            onJoinSuccess(msg);
        });
    }
    function joinNotification(channel, cb) {
        console.log(`emit join for channel:${JSON.stringify(channel)}`);
        socket.emit('joinnotificationpharmacy', channel, cb);
    }
    function registerNotificationHandler(onNotificationReceived) {
        socket.on('notification', (notification) => {
            onNotificationReceived(notification);
        });
    }
    socket.on('error', (err) => {
        console.log('received socket error:');
        console.log(err);
    });

    return {
        registerDisconnectHandler,
        registerNotificationHandler,
        registerJoinHandler,
        joinNotification
    };
}

Server side:

const socketIo = require('socket.io');

class SocketService {
    constructor(server) {
        this.io = socketIo(server);
        this.io.on('connect', socket => {
            socket.on('joinnotificationpharmacy', function(data){
                console.log('JOIN CLIENTID:'+data+" SOCKET ID:"+socket.id);
                socket.join(data);
                socket.emit('joinsuccess', socket.id);
            });
            socket.on('disconnect', function(){
                console.log("client has disconnected:"+socket.id);
            });
        });
        this.io.on('connect_error', function(err) {
            // handle server error here
            console.log('Error connecting to server');
        });
    }

    sendSocketNotification(receiver, notification){
        this.io.sockets.in(receiver).emit('notification', notification);
    }
}

module.exports = SocketService;

Logs on the server side indicates that one client joined server, but when refresh happens, multiple users are disconnected, which looks weird to me and looks like potential problem:

server side logs:

JOIN CLIENT:5f6cb94d1bb5adbab7866a14 ID:p9hC0OTKztwGlalWAAAa
client has disconnected:fJKV3MkJ4bxemZ4sAAAD
client has disconnected:xJ7bJvSqwXBbkxOhAAAI
client has disconnected:HkwouVqEXBeKvkHsAAAG
client has disconnected:9dd6e8huPveN8aVjAAAe
client has disconnected:jAZy4FXTC2oRd0a_AAAd
client has disconnected:Doonmd-ogmIRXU7iAAAc
client has disconnected:G-tc_RbO_99mivQyAAAb
client has disconnected:p9hC0OTKztwGlalWAAAa
client has disconnected:OYo2uOxuFzMeYtUEAAAW
client has disconnected:54HLS2-KHAWJ5NmqAAAR
client has disconnected:92veurywX4LhfK4cAAAg
client has disconnected:Ox73Zt-lMB0rFeA-AAAf

I would appreciate some advice about how to properly handle disconnection on the client side and reinitialize connection.

UPDATE:

I've found a workaround for this problem, though if someone has some better idea, it's still welcome. I've added a code that identifies page reload and changes redux state which state to identify that connection doesn't exist anymore.

  useEffect(() => {
    if (sessionStorage.getItem('is_reloaded')) {
      console.log('Reloaded!');
      dispatch(disconnectNotificationsSocket(globalSocket, notifications.activeSocket));
    }
    sessionStorage.setItem('is_reloaded', true);
  }, []);

Upvotes: 2

Views: 16201

Answers (2)

Eduardo Luis Santos
Eduardo Luis Santos

Reputation: 496

You can ensure to connect and disconnect only once with a code like this:

useEffect(() => {
    const socketService = new SocketService();

    console.log('mount it!');
    
    return function cleanup() {
        socketService.disconnect();
    }
}, []);

In this case, SocketService is a simple class to make easy the use of a socket:

import {io} from "socket.io-client";


const ENDPOINT = 'ws://localhost:5000/';

export default class SocketService {
    private socket: any = {};

    constructor(private emisor_id?: string) {
        this.socket = io(ENDPOINT);
    }

    public send = (message: string) => {
        this.socket.emit('postMessage', message)
    }

    // disconnect - used when unmounting
    public disconnect (): void {
        this.socket.disconnect();
    }
}

On the server-side you should see something like:

connection
client disconnected

Upvotes: 0

proxim0
proxim0

Reputation: 1538

Just to point out a few things (docs could be better here)

socket.on('disconnect', () => {
            console.log('Disconnected');
            onDisconnect();
        });

Should not be on the client side, this is reserved for the server when a user disconnects.

When you 'reload' the page this triggers a 'disconnect' event every time, this is default behavior. Your script to connect on the client side should just run again and re-join the socket server just as it did originally, albeit, with a different socket. So you shouldn't really need to think too much about that, think about it like they are just joining again, not 'rejoining'.

This is really geared toward page reloads, now if the user gets disconnected, say they dropped internet or something like that, this is where the socket.io beforeReconnect and reconnect come in, it will keep trying to ping the server to try and reconnect. In this case you would put custome code to reconnect but again, if they are reloading the page it should work.

Upvotes: 6

Related Questions