ReactJS - this.functionname is not a function

2019-08-09 07:48发布

问题:

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?

回答1:

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.



回答2:

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 :)