I have a react based form with more than 10 fields in it, I have states for those ten form
fields (controlled component).
Most of these input
fields are of type text
only but other types fields will be added later.
Problem is that I need to write 10 change handlers for them to set state correctly and then add method bindings for each of them in constructor.
I am quite new to react and may be not aware about correct methodologies and techniques.
Please guide me to how to improve my current code structure and avoid writing boiler plates and repetitive error prone code.
My current Registration component is like below -
export default class Register extends Component {
constructor(props){
super(props);
this.state = {
regName : '',
regAdd1 : '',
regAdd2 : '',
regState : '',
regZipCode : '',
regCity : '',
regPhone : ''
};
// add bindings .... ugh..
this.changeRegAdd1 = this.changeRegAdd1.bind(this);
this.changeRegAdd2 = this.changeRegAdd2.bind(this);
//Similary binding for other handlers...
}
// add individual field change handlers ... ugh...
changeRegName(e) {
this.setState({regName:e.target.value});
}
changeRegAdd1(e) {
this.setState({regAdd1:e.target.value});
}
changeRegAdd2(e) {
this.setState({regAdd2:e.target.value});
}
changeRegState(e) {
this.setState({regState:e.target.value});
}
// Similary for other change handler ....
handleSubmit(e) {
e.preventDefault();
// validate then do other stuff
}
render(){
let registrationComp = (
<div className="row">
<div className="col-md-12">
<h3>Registration Form</h3>
<fieldset>
<div className="form-group">
<div className="col-xs-12">
<label htmlFor="regName">Name</label>
<input type="text" placeholder="Name"
onChange={this.changeregName} value = {this.state.regName} className="form-control" required autofocus/>
</div>
</div>
<div className="form-group">
<div className="col-xs-12">
<label htmlFor="regAdd1">Address Line1</label>
<input
type = "text"
placeholder = "Address Line1"
onChange = {this.changeregAdd1}
value = {this.state.regAdd1}
className = "form-control"
required
autofocus
/>
<input
type = "text"
placeholder = "Address Line2"
onChange = {this.changeregAdd2}
value = {this.state.regAdd2}
className = "form-control"
required
autofocus
/>
</div>
</div>
<div className="form-group">
<div className="col-xs-6">
<label htmlFor="regState">State</label>
<input
type = "text"
placeholder = "State"
onChange = {this.changeregState}
value = {this.state.regState}
className = "form-control"
required
autofocus
/>
</div>
<div className="col-xs-6">
<label htmlFor="regZipCode">Zip Code</label>
<input
type = "text"
placeholder = "Zip Code"
onChange = {this.changeregZipCode}
value = {this.state.regZipCode}
className = "form-control"
required
autofocus
/>
</div>
</div>
<div className="form-group">
<div className="col-xs-12">
<label htmlFor="regCity">City</label>
<input
type = "text"
placeholder = "City"
title = "City"
onChange = {this.changeregCity}
value = {this.state.regCity}
className = "form-control"
required
autofocus
/>
</div>
</div>
{/* other form fields */}
</fieldset>
</div>
</div>
);
return registrationComp;
}
}
There can be other lots of ways to do it which I may not be aware.
But I prefer to do change handling in a common method for certain type of common fields such as <input type of text />
This is a sample input field -
<input
onChange = {this.onChange}
value = {this.state.firstName}
type = "text"
name = {"firstName"}
/>
I keep both state's field name and input's "name" attribute same. After that I write a common change handler for all such fields.
Need to write just one line in change handler.
onChange(e) {
this.setState({[e.target.name]: e.target.value});
}
I am using es6's dynamic property setting from string as property name
{ ["propName] : propValue }.
To avoid writing manual binding for methods in react component you can follow below two approaches -
create a method like this in your component.
_bind(...methods) {
methods.forEach( (method) => this[method] = this[method].bind(this) );
}
use _bind
to bind your methods.
constructor(props){
super(props);
this.state = {
regName : '',
regAdd1 : '',
regAdd2 : '',
regState : '',
regZipCode : '',
regCity : '',
regPhone : ''
};
// add bindings .... ugh..
//this.changeRegAdd1 = this.changeRegAdd1.bind(this);
//this.changeRegAdd2 = this.changeRegAdd2.bind(this);
//Similary binding for other handlers...
this._bind(
'changeRegName',
'changeReg1' , 'changeRegAdd2'
// and so on.
);
}
EDIT :
Adding validation -
set a state to indicate that form has errors. Specific error details can be found in state's error object.
validateInput() {
let errors = {};
Object.keys(this.state)
.forEach((stateKey) => {
isEmpty(this.state[stateKey]) ? (errors[stateKey] = `*required` ) : null;
});
return {
errors,
isValid : isEmptyObj(errors)
};
}
isFormValid() {
const { errors, isValid } = this.validateInput();
if (!isValid) {
this.setState({ errors});
}
return isValid;
}
onSubmit(e) {
e.preventDefault();
this.setState({errors : {}});
if (this.isFormValid()) {
// Perform form submission
}
}
I have used to utility method calld isEmpty
, isEmptyObj
. They just check if object is null or undefined or field is empty.
I hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With