Bomber
Bomber

Reputation: 10947

ReactJS - this.functionname is not a function

I have a listener setup in my componentDidMount:

updateBasketTotal: function() {
    BasketService.getBasketTotal(function(data){
        this.setState({
            selectedPeopleCount: data.TotalMembers
        });
    }.bind(this));
},

componentDidMount: function() {
    this.updateBasketTotal();
    this.subscribeToChannel(basketChannel,"selectAll",this.listenerSelectAll);
    this.subscribeToChannel(basketChannel,"removePersonFromBasket",this.listenerRemovePersonFromBasket);
    this.subscribeToChannel(basketChannel,"addPersonToBasket",this.listenerAddPersonToBasket);
    this.subscribeToChannel(basketChannel,"addArrayToBasket",this.listenerAddArrayToBasket);
},

listenerAddArrayToBasket: function(data){
    BasketService.addPerson(data.arrayToPush,function(){
        this.updateBasketTotal();
    });
},
listenerAddPersonToBasket: function(data){
    BasketService.addPerson(data.personId,function(){
        this.updateBasketTotal();
    });
},
listenerRemovePersonFromBasket: function(data){
    BasketService.removePerson(data.personId,function(){
       this.updateBasketTotal();
    });
},
listenerSelectAll: function(data){
    BasketService.selectAll(data.selectAll, function () {
        this.updateBasketTotal();
    });
}

However, if I publish a message when I'm not on this page, I get an error:

this.updateBasketTotal is not a function

Can anyone please tell me how I can use this.updateBasketTotal?

I think its a problem with 'this' but not sure how to fix it. Thanks in advance

UPDATE:

Have tried adding bind() to the listener:

    listenerAddPersonToBasket: function(data){
    BasketService.addPerson(data.personId,function(){
        this.updateBasketTotal();
    }.bind());
},

But no joy, any ideas?

Upvotes: 1

Views: 931

Answers (2)

Ed Ballot
Ed Ballot

Reputation: 3485

I assume your component is unsubscribing to those channels in componentWillUnmount to avoid resource leaks and duplicate subscriptions.

The asynchronous callbacks should call isMounted to ensure the component is still mounted before attempting anything else.

BasketService.selectAll(data.selectAll, function () {
    if (this.isMounted()) {
        this.updateBasketTotal();
    }
}.bind(this));

I don't know if the isMounted check will solve your problem since that may also not be a function anymore. If it isn't, you might consider adding your own property to track whether the component is mounted or not and check that rather calling a function.

Upvotes: 2

Vlad Miller
Vlad Miller

Reputation: 2279

listenerAddArrayToBasket: function(data) {
    var _this = this;
    BasketService.addPerson(data.arrayToPush,function() {
        _this.updateBasketTotal();
    });
}

or

listenerAddArrayToBasket: function(data) {
    BasketService.addPerson(data.arrayToPush,function() {
        this.updateBasketTotal();
    }.bind(this));
}

React does not bind context by default, you have to do it yourself. In your example this would refer to callback function, not to react object. You can either assign react context to a variable and use it inside your callback, or bind context directly to callback.

Here is the great article explaining contexts in JavaScript

Also in ES6 it's possible to use double arrow declaration

class SomeComponent extends React.Component {

  updateBasketTotal() {
    ....
  }

  lisneterAddArrayToBasket() {
    BasketService.addPerson(data.arrayToPush, () => {
      this.updateBasketTotal()
    })
  }

}

You can use babel to compile your ES6 code to old plain ES5 :)

Upvotes: 0

Related Questions