Pass multiple parameters to event handlers in React
So im trying to make react work with ES6 syntax. In ES5 I had setInitialState without a constructor which worked with the function binding syntax. I have a list of prices which is arbitrary and I want the state to change when the input element is changed. But the right price has to be changed.
I'm not even quite sure this is the right way to do it. Can someone please tell me the most recent way this should be done?
Here is my code:
import React, {Component} from 'react'
import 'bootstrap/dist/css/bootstrap.css';
export default class PriceTable extends Component {
constructor(props, context) {
super(props, context);
this.state = {
pid: this.props.pid || "12345",
name: this.props.name || "name",
prices: this.props.prices || [
{count: 1, desc: 'one', price: 8.25},
{count: 6, desc: 'six', price: 7.60},
{count: 12, desc: 'twelve', price: 6.953}
]
};
this.setPid = this.setPid.bind(this);
this.setName = this.setName.bind(this);
this.setCount = this.setCount.bind(this, i);
this.setDesc = this.setDesc.bind(this, i);
this.setPrice = this.setPrice.bind(this, i);
this.logDebug = this.logDebug.bind(this);
}
setPid(e) {
this.setState({pid: e.target.value})
}
setName(e) {
this.setState({name: e.target.value})
}
setCount(i, e) {
var newPrices = this.state.prices
newPrices[i].count = e.target.value
this.setState({prices: newPrices})
}
setDesc(i, e) {
var newPrices = this.state.prices
newPrices[i].sec = e.target.value
this.setState({prices: newPrices})
}
setPrice(i, e) {
var newPrices = this.state.prices
newPrices[i].price = e.target.value
this.setState({prices: newPrices})
}
_renderPriceRow(price, i) {
return (
<tr key={i}>
<td >
<input type="text" className="form-control" defaultValue={price.count} onChange={this.setCount(this, i).bind(this, i)}/>
</td>
<td >
<input type="text" className="form-control" defaultValue={price.desc} onChange={this.setDesc(this, i).bind(this, i)}/>
</td>
<td >
<input type="text" className="form-control" defaultValue={price.price} onChange={this.setPrice(this, i).bind(this, i)}/>
</td>
</tr>
);
}
render() {
return (
<div className="row">
...
</div>
);
}
}
And this is the error...
PriceTable.jsx:21 Uncaught ReferenceError: i is not defined
at new PriceTable (PriceTable.jsx:21)
You are binding the functions incorrectly
In your constructor you need not specify the argument, you only need to bind it like this.setDesc = this.setDesc.bind(this);
Also In your onChange, when you want to pass paramters to the function you specify it with bind and not pass as arguments and bind them again .
onChange={this.setDesc.bind(this, i)}
You entire code will look like
import React, {Component} from 'react'
import 'bootstrap/dist/css/bootstrap.css';
export default class PriceTable extends Component {
constructor(props, context) {
super(props, context);
this.state = {
pid: this.props.pid || "12345",
name: this.props.name || "name",
prices: this.props.prices || [
{count: 1, desc: 'one', price: 8.25},
{count: 6, desc: 'six', price: 7.60},
{count: 12, desc: 'twelve', price: 6.953}
]
};
this.setPid = this.setPid.bind(this);
this.setName = this.setName.bind(this);
this.setCount = this.setCount.bind(this);
this.setDesc = this.setDesc.bind(this);
this.setPrice = this.setPrice.bind(this);
this.logDebug = this.logDebug.bind(this);
this._renderPriceRow = this._renderPriceRow.bind(this);
}
setPid(e) {
this.setState({pid: e.target.value})
}
setName(e) {
this.setState({name: e.target.value})
}
setCount(i, e) {
var newPrices = this.state.prices
newPrices[i].count = e.target.value
this.setState({prices: newPrices})
}
setDesc(i, e) {
var newPrices = this.state.prices
newPrices[i].sec = e.target.value
this.setState({prices: newPrices})
}
setPrice(i, e) {
var newPrices = this.state.prices
newPrices[i].price = e.target.value
this.setState({prices: newPrices})
}
_renderPriceRow(price, i) {
return (
<tr key={i}>
<td >
<input type="text" className="form-control" defaultValue={price.count} onChange={this.setCount.bind(this, i)}/>
</td>
<td >
<input type="text" className="form-control" defaultValue={price.desc} onChange={this.setDesc.bind(this, i)}/>
</td>
<td >
<input type="text" className="form-control" defaultValue={price.price} onChange={this.setPrice.bind(this, i)}/>
</td>
</tr>
);
}
render() {
return (
<div className="row">
...
</div>
);
}
}
You could also make use of Arrow functions to pass parameters like
onChange={(e) => this.setDesc(e, i)}
Use the onChange
method like this:
onChange={this.setCount.bind(this, i)}
onChange={this.setDesc.bind(this, i)}
onChange={this.setPrice.bind(this, i)}
Remove these lines from constructor
:
this.setCount = this.setCount.bind(this, i);
this.setDesc = this.setDesc.bind(this, i);
this.setPrice = this.setPrice.bind(this, i);
We can bind
the function
in constructor
, where no extra parameter is required, like the these functions
in your case:
this.setPid = this.setPid.bind(this);
this.setName = this.setName.bind(this);
this.logDebug = this.logDebug.bind(this);
But if in any function
you want to pass any extra parameter, then you need to use either arrow function
or bind
that function
by using .bind(this, parameter)
.
Note:
Another thing that you need to change here is, the way you are updating the state
values, You should never mutate state variable directly. When you assign an array
to any variable, that variable will have the reference of that array
, means whatever changes you will do to that variable will affect the original array
.
So you need to create a copy of that variable and then do the changes on that and use setState
to update the values.
As per DOC:
Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.