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