user5965280
user5965280

Reputation:

React: 'this.state' of parent component is undefined inside a child component function

I am developing an Order form in ReactJS for online shop. Now I get the message in browser console like 'this.state' is undefined inside a component function ... What is wrong? How can I avoid the problem? I did not find any clues in official documentation.

class Service extends React.Component {
    constructor(props){
        super(props);
        this.state  = {active:      false,}
    }
    clickHandler(){
        let active = !this.state.active;
        this.setState({active: active});
        this.props.addTotal((active == true ? this.props.price : -this.props.price));
    }
    render(){
        return(
            <p className={this.state.active ? 'active' : ''} onClick={() => this.clickHandler()}>
                {this.props.name} <b>${this.props.price.toFixed(2)}</b>
            </p>
        );  
    }
};

class OrderForm extends React.Component {
    constructor(){
        super();
        this.state  = { total:  0,}
    }   
    addTotal(price){
        this.setState({total: this.state.total + price});
    }
    serviceList(){
        var self    =   this;
        //Iteration with map method
        const serviceMapIterator = this.props.services.map(function(item, i, arr){
                return (<Service    key     =   {i.toString()} 
                                    name    =   {item.name} 
                                    price   =   {item.price} 
                                    active  =   {item.active} 
                                    addTotal=   {self.addTotal} >
                        </Service>);
        });
        return serviceMapIterator;
    }
    render(){
        let service =   this.serviceList();
        return(
            <div>
                {service}
                <p id={'total'}>Total <b>${this.state.total.toFixed(2)}</b></p>
            </div>
        );
    }
};
var services = [
    { name: 'Web Development',  price: 300 },
    { name: 'Design', price: 400 }
];

How can I change it? What is the problem?

Upvotes: 1

Views: 1475

Answers (2)

Roman
Roman

Reputation: 21775

You call the addTotal() method defined in OrderForm (parent class) from Service (child class). You need addTotal have an access to "state" of the parent's class. So you are going to change your code either for constructor() by adding a row with .bind(this):

    constructor(){
        super();
        this.state  = { total:  0,};
        this.addTotal = this.addTotal.bind(this);
    }

or for serviceList() method by adding .bind(this) after the .map() of javascript method. Please, see below:

    serviceList(){
        var self    =   this;
        //Iteration with map method
        const serviceMapIterator = this.props.services.map(function(item, i, arr){
                return (<Service    key     =   {i.toString()} 
                                    name    =   {item.name} 
                                    price   =   {item.price} 
                                    active  =   {item.active} 
                                    addTotal=   {self.addTotal} >
                        </Service>);
        }.bind(this));
        return serviceMapIterator;
    }

Upvotes: 0

Ritesh Bansal
Ritesh Bansal

Reputation: 3248

When addTotal is called, this does not refer to the context of OrderForm Component. You should bind addTotal function like this in the constructor:

constructor() {
  ...
  this.addTotal = this.addTotal.bind(this)
}

Your addTotal function looks like this:

addTotal(price){
        this.setState({total: this.state.total + price});
}

As per DOCS

You should not write

this.setState({total: this.state.total + price});

You should use the second form of setState that accepts a callback function which receives previousState and previousProps as an argument.

addTotal(price) {
  this.setState((prevState, previousProps) => ({
    counter: prevState.total + price
  }));
}

Similar change is required in clickHandler function.

Upvotes: 1

Related Questions