2

I am building a Backbone.js application, I use BackboneJS Radio for messaging.

First I created a channel:

App.actionsChannel = Backbone.Radio.channel('actions');

And when I click an action button, let's say 'next' action button:

App.actionsChannel.trigger('action:triggered', 'next');

And I handle the action:

App.actionsChannel.on('action:triggered', function(actionName){
  //do some ajax requests
});

The problem is, when I click the next button for the first time, it triggers the next action one time, and the second time, it triggers twice, the third time it triggers 4 times, and so on...

Every time I trigger the next action, it fires many times, not once. And when I checked the actionsChannel._events, I found it contains all the actions I triggered.

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
kdureidy
  • 960
  • 9
  • 26

1 Answers1

3

It's because the registering on is done multiple times, somewhere not shown in your question, and it should only be done once.

✘ Don't do this

var view = Backbone.View.extend({
    events: {
        "click": "onClick"
    },

    onClick: function(e) {
        App.actionsChannel.on('action:triggered', function(actionName) {
            //do some ajax requests
        });
    }
});

✔ Do this

var view = Backbone.View.extend({
    events: {
        "click": "onClick"
    },

    initialize: function(){
        App.actionsChannel.on('action:triggered', this.onActionTriggered);
    },

    onClick: function(e) {
        // or if you must register it here for example.
        // First make sure it's unregistered.
        App.actionsChannel.off('action:triggered', this.onActionTriggered);
        App.actionsChannel.on('action:triggered', this.onActionTriggered);
    },

    onActionTriggered: function(actionName) {
        //do some ajax requests
    },
});

Using the on function multiple times just adds another listener to the list. So, when triggered, the callback is called as much times as it was registered.

The best

It is recommended to use listenTo instead of on whenever possible to avoid memory leaks.

Backbone.js on vs listenTo

var view = Backbone.View.extend({
    events: {
        "click": "onClick"
    },

    initialize: function(){
        // this will be removed automatically when the view is `remove`d,
        // avoiding memory leaks.
        this.listenTo(App.actionsChannel, 'action:triggered', this.onActionTriggered);
    },

    onClick: function(e) {

    },

    onActionTriggered: function(actionName) {
        //do some ajax requests
    },
});

The code snippets above are just examples of how to listen to an event. Use trigger where you need it and where it make sense.

Community
  • 1
  • 1
Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
  • I only use the `on` function in one place. I have two views, one triggers the event, and other catches it on the initialize – kdureidy Sep 27 '16 at 06:37
  • I think the issue is that I have zombie views, when the route changes, a new view created before the current view that catches the event is destroyed – kdureidy Sep 27 '16 at 06:40
  • Favor `listenTo` over `on` for that reason. – Emile Bergeron Sep 27 '16 at 07:03
  • @kdureidy even if you have the `on` on one place, my answer is still on point, it's called more than once. A zombie view is just a case where something might be called more than expected. – Emile Bergeron Sep 27 '16 at 14:41
  • I will try your solution, but where in your code do you use `trigger`? – kdureidy Sep 28 '16 at 07:54
  • I don't think listenTo will help in this case, since the action name may always be the same `next` – kdureidy Sep 28 '16 at 07:55
  • 1
    @kdureidy `listenTo` removes listener of a view (or object) when it's destroyed, or when you call `this.stopListening()`. On the other hand, each call to `on` needs to be individually removed with `off` if you don't want to break the "wiring" and avoid memory leaks. – Emile Bergeron Sep 28 '16 at 14:24