5
\$\begingroup\$

I am upgrading a traditional jQuery based app by converting a few parts of it into ReactJs components and use Socket.io for the data transfer medium.

There are a few React components spread across the page. The React components will receive updates from the socket connection in their componentDidMount method.

For example:

componentDidMount: function(){
    socket.on('connect', function(data){....});
    socket.on('anotherEvent', function(data){....})
}

Now, the thing is that the React components will be scattered across the page and will not be under a single app structure, and they will be subscribing to the same socket events to stay in sync.

For this, I wrote the below Socket.io wrapper, this way multiple subscriptions for the same events can be avoided but all the interested React components can be notified on an event update.

window.socketEvents = (function(){
    var socket = io.connect("http://localhost:3000"),
        events = {},                    //events structure = {'event_name':subscribers:[callback functions], isSubscribed:true}
        callOnConnect = [];


    var _register = function(event, callback){
        events[event] = events[event] || {};
        events[event].subscribers = events[event].subscribers || [];
        events[event].subscribers.push(callback);
    };

    var _subscribe = function(event){
        console.log('Subscribingto: ', event);
        socket.on(event, function(data){    // notifiy the subscribers of this event by firing their callbacks.
            console.log('received evt: ', data);
            for(var cb=0;cb<events[event].subscribers.length;cb++){
                events[event].subscribers[cb](data);
            };
        });
        events[event].isSubscribed = true;
    };  

    var _emit = function(event, data, onConnect){
        console.log('_emit called for: ', event);
        socket.emit(event, data);
        if(onConnect){
            callOnConnect.push(function(){
                socket.emit(event, data);
            });
        }
    };

    var _listen = function(event, callback){
        _register(event, callback);
        if(!events[event].isSubscribed){
            _subscribe(event);
        };
    };

    setTimeout(function(){  // register a little late to avoid multiple callbacks.
        socket.on('connect', function(){
            console.log('connect fired');
            for(var i=0;i<callOnConnect.length;i++){
                callOnConnect[i]();
            }
        });
    },1000);

    return {
        listen: _listen,
        emit: _emit
    }

})();

React example with the new wrapper:

In ReactClassOne:

componentDidMount: function(){
    socketEvents.emit('join_service', {name:'Some name'}, true); // the third boolean param states that should the event be emitted if the socket disconnects and then reconnects.
    socketEvents.listen('anotherEvent', function(data){...})
}

In ReactClassTwo:

componentDidMount: function(){
    socketEvents.emit('join_service', {name:'Some name'}, true);
    socketEvents.listen('anotherEvent', function(data){...})
}

The code is currently working fine during the preliminary development but I am worried about the timeout in the socketEvents. So, I would like this to be reviewed.

\$\endgroup\$
2
\$\begingroup\$

It looks like you've written the code that already exists.

this way multiple subscriptions for the same events can be avoided but all the interested React components can be notified on an event update

Why would you want to avoid this? There is no overhead in a function call. In fact, this is exactly how Socket.io client works (based on Emitter component):

Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
  this._callbacks = this._callbacks || {};
  (this._callbacks['$' + event] = this._callbacks['$' + event] || [])
    .push(fn);
  return this;
};

Then there is this hack. If the client will connect in less than a second, you'll miss 'connect' event:

setTimeout(function(){  // register a little late to avoid multiple callbacks.
    socket.on('connect', function(){
        console.log('connect fired');
        for(var i=0;i<callOnConnect.length;i++){
            callOnConnect[i]();
        }
    });
},1000);

Last, it's a good practice to unsubscribe from events in componentWillUnmount. Your wrapper doesn't allow this.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.