Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid adding field change handling methods and other boilerplates in react form?

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;
    }
}
like image 358
Sudhir Mishra Avatar asked Mar 08 '23 17:03

Sudhir Mishra


1 Answers

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 -

  1. Use es6 arrow functions
  2. A hack ( I don't know whether this approach is fast or slow but It works :) )

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 -

  1. Write a method which can iterate over state keys and check if state is empty.
  2. If any of the input field is empty collect the detail about that input and mark that required.
  3. 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.

like image 175
WitVault Avatar answered Mar 11 '23 07:03

WitVault