Signalr: JS client hubProxy.off( eventName, handler) doesn't completely remove the handler

Created on 8 Jan 2013  路  11Comments  路  Source: SignalR/SignalR

e.g. for below sample, after call myHub.on('foo2', handler) 3 times, then call myHub.off('foo2', handler) one time or many times, and then server call Clients.All.foo2(), on client the handle on foo2 still raise 2 times.

Note, in jquery .off completely remove the specific event handlers.

                myHub.on('foo2', handler);
                myHub.on('foo2', handler);
                myHub.on('foo2', handler);
                myHub.off('foo2', handler);
Investigate Bug

All 11 comments

Not critical for 1.0 RTW.

This is still a problem especially for connections that provide multiple hub endpoints. Consumers that register can never unregister.

Hello, is there an update on this? We have clients connect to multiple hubs depending on where they navigate to in our single page client application. When they navigate away, we want to disconnect them from the unnecessary hubs, to avoid them receiving updates and downloading data they no longer need.

Doesn't seem like this code has changed in a few years so it is unlikely it was fixed.

Hmm thanks moozzyk. A core group of our users keep their browsers open 24/7 (in a control system environment) so having an ability to permanently disconnect from a particular hub would be great.

Hi Stefan,

the problem is the generated HubProxy.js file. It is really just a generally starting point that does not adequately support individual hub instances out of the box.
I, like you, work in a complex Spa environment loading coal (of many colors) into dam big boats.
To make signalR work with multiple hubs we need to make some minor changes to the default HubProxy.js file.

Where the proxies are created I have added an additional bool property called connectHub.

ie:

$.hubConnection.prototype.createHubProxies = function () {
var proxies = {};
this.starting(function () {
// Register the hub proxies as subscribed
// (instance, shouldSubscribe)
registerHubProxies(proxies, true);

        this._registerSubscribedHubs();
    }).disconnected(function () {
        // Unsubscribe all hub proxies when we "disconnect".  This is to ensure that we do not re-add functional call backs.
        // (instance, shouldSubscribe)
        registerHubProxies(proxies, false);
    });


    proxies['stockpileHub'] = this.createHubProxy('stockpileHub');
    proxies['stockpileHub'].client = {};
    proxies['stockpileHub'].connectHub = false;

Then I have modified the registerHubProxies to use this property when connecting to the server:

ie:

function registerHubProxies(instance, shouldSubscribe) {
var key, hub, memberKey, memberValue, subscriptionMethod;

    for (key in instance) {
        if (instance.hasOwnProperty(key)) {
            hub = instance[key];

            if (!(hub.hubName)) {
                // Not a client hub
                continue;
            }

            if (shouldSubscribe) {
                // We want to subscribe to the hub events
                if (hub.connectHub !== undefined && hub.connectHub) {
                    subscriptionMethod = hub.on;
                } else {
                    subscriptionMethod = hub.off;
                }
            } else {
                // We want to unsubscribe from the hub events
                subscriptionMethod = hub.off;
            }

            // Loop through all members on the hub and find client hub functions to subscribe/unsubscribe
            for (memberKey in hub.client) {
                if (hub.client.hasOwnProperty(memberKey)) {
                    memberValue = hub.client[memberKey];

                    if (!$.isFunction(memberValue)) {
                        // Not a client hub function
                        continue;
                    }

                    subscriptionMethod.call(hub, memberKey, makeProxyCallback(hub, memberValue));
                }
            }

        }
    }
}

I have also added an additional method that can be used to reset the connectHub variable.

ie:

$.hubConnection.prototype.resetHubProxies = function () {
var key;
for (key in this.proxies) {
this.proxies[key].connectHub = false;
}
}

I then consume it like this:

    $.connection.hub.stop();
    (($.connection.hub) as any).resetHubProxies();
    $.connection.trainServiceHub.connectHub = true;
    var hubStart = $.connection.hub.start({ waitForPageLoad: false }).then(() => callback(this));

Hope this help your project.

kind regards
Bazza

Thanks Bazza :)

What I ended up doing is wrapping the client in an observable stream. The observable manages the subscriptions and when there are no subscriptions left it will unbind from the client proxy. myHub.off('foo2', null); will unsubscribe all handlers from foo2. That actually works.

This issue has been closed as part of issue clean-up as described in https://blogs.msdn.microsoft.com/webdev/2018/09/17/the-future-of-asp-net-signalr/. If you're still encountering this problem, please feel free to re-open and comment to let us know! We're still interested in hearing from you, the backlog just got a little big and we had to do a bulk clean up to get back on top of things. Thanks for your continued feedback!

I had this issue when i logged out i found the listener fired may times even i disconnected from the hub so you just need to clear the object
delete service.hub.connection.proxies.yourhubname;//the hub name in lower case delete window.jQuery.hubConnection;

FYI:

myHub.off('foo2', null); works for me. It will remove all duplicated events if any.

Was this page helpful?
0 / 5 - 0 ratings