How To Make Interactive ReactJS Form

Posted on January 13, 2019


Users who visit your web application have a particular goal in mind that they want to accomplish. A form is a medium that allows your users to get in contact with you and send information, such as an order, a catalog request, or even a query, which is passed on to other processes.

A good form design that is clear and smart can help your users to achieve their goal fast. On the contrary, a badly designed form will cause confusion and even discourage users from interacting with your application.

So we agree that a good form benefits your application and make users happy. Yet implementing a good form requirements seems hard in React: dynamic forms, real-time responsive feedback, and creating a nice UX. How do we work these requirements in the land of components, states and props?

The first hint that we can get is of course from React documentation about forms.

handleChange = e => {
  this.setState({ value: e.target.value })
}

// ...

<input
  onChange = {this.handleChange}
  value = {this.state.value}
/>

This is basically the summary of React's form documentation. It simply tells you that this is how React should be used in handling users click or keystroke. React sets user's value to state and then use that state as value for the input. The end.

Huh? That's it?

Yup. As for the rest of the issues you will face when building form for an application with complex business logic.. well, they are up to you. Like doing:

  1. Validation
  2. Displaying errors
  3. Keeping track of form fields
  4. Handling submission

As it's read in the documentation, React is very unopinionated about how you might structure your project and choose your library stack. That also means it just provide the very basic necessity in making form components. component, state, props are just like puzzle blocks, and we need to piece them together by ourselves.

Here's the final product you get from this tutorial:

There are 3 basic principles you need to remember when making forms with React, they are:

  1. component is used for rendering form elements, normally JSX elements
  2. state is used to keep track of user's inputs
  3. props is used for passing data into JSX elements

No matter what kind of form you are trying to create, as long as you remember these 3 basic principles, you'll be fine.

A Basic React Form

Everything in React is a component, including a form, and React used state to keep track of input values. Here is an example form written in React.

class BasicForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name:'',
      email: '',
    };
  }

  handleNameChange = (event) => {
    this.setState({name: event.target.value});
  }

  handleEmailChange = (event) => {
    this.setState({email: event.target.value});
  }

  handleSubmit = (event) => {
    event.preventDefault();
    const { name, email } = this.state
    alert(`Your state values: \n 
            name: ${name} \n 
            email: ${email}`)
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <div className="form-group">
          <label htmlFor="name">Name</label>
          <input name="name" className="form-control" id="name" placeholder="Enter name" value={this.state.name} onChange={this.handleNameChange} />
        </div>
        <div className="form-group">
          <label htmlFor="email">Email</label>
          <input name="email" className="form-control" id="email" placeholder="Enter email" value={this.state.email} onChange={this.handleEmailChange} />
        </div>
        <button type="submit" className="btn btn-success btn-block">Submit</button>
      </form>
    );
  }
}

Whoa! What does this code do?

Don't worry, the code won't bite! Let me explain them to you now.

We'll start from state. A react form uses state as the single source of truth for field values. That means every input element you'll have on your form component will take state value as its value.

this.state = {
  name:'',
  email: '',
};   

State values are then assigned into input tags value prop. We also add an onChange prop that will run every time the input value is changed. Lastly, we also add onSubmit prop into our form component for handling submission.

render() {
  return (
    <form onSubmit={this.handleSubmit}>
      <div className="form-group">
        <label htmlFor="name">Name</label>
        <input name="name" className="form-control" id="name" placeholder="Enter name" 
        // value and onChange prop
          value={this.state.name} 
          onChange={this.handleNameChange} />
      </div>
      <div className="form-group">
        <label htmlFor="email">Email</label>
        <input name="email" className="form-control" id="email" placeholder="Enter email" 
        // value and onChange prop
          value={this.state.email} 
          onChange={this.handleEmailChange} />
      </div>
      <button type="submit" className="btn btn-success btn-block">Submit</button>
    </form>
  );
}    

Next, we can add a handleChange method which accepts the event argument. This event object will hold our input name and value.

handleNameChange = (event) => {
  this.setState({name: event.target.value});
}

handleEmailChange = (event) => {
  this.setState({email: event.target.value});
}

The last part of a form structure is the submit handler method. In this example, we used a handleSubmit method that simply call an alert box which print out our state values.

handleSubmit = (event) => {
  event.preventDefault();
  const { name, email } = this.state
  alert(`Your state values: \n 
          name: ${name} \n 
          email: ${email}`)
} 

Like a regular HTML form, this is where the saving or sending data is executed and processed. Since we're using our own JavaScript code to handle submission, we need to add event.preventDefault() into our submission method. This is because browser's JavaScript listener is set to listen to form submit event, which usually triggers a page reload. By using this preventDefault, we are telling the browser to stop doing whatever default method it does. That way the page reload will be stopped and our submission method can run.

Making validations

The traditional approach to validate data is by submitting the form, wait for the server to finish validating, then the web page will refresh with some error message. The process takes a lot of time and cumbersome for users.

Since React is a front-end library, it can solve this problem by building instant validation into form component. In fact, this is a common pattern in React application, and it's very awesome in my opinion.

Since React store all form data in the state, we can use a bit of checking before render and display error message if data isn't valid. For an example, to validate if name length is more than 3 characters, we can use:

render(){
  const isValidName = this.state.name.length > 3
  const isValidEmail = this.state.email.length > 3
}

Then to put it in context:

// the render method

render() {
  const isValidName = this.state.name.length > 3;
  const isValidEmail = this.state.email.length > 3;
  return (
    <form onSubmit={this.handleSubmit}>
      <div className="form-group">
        <label htmlFor="name">Name</label>
        <input
          name="name"
          className={`form-control ${ isValidName? '':'is-invalid' }`}
          id="name"
          placeholder="Enter name"
          value={this.state.name}
          onChange={this.handleNameChange}
        />
        {/*feedback here*/}
      { isValidName? null: <div className='invalid-feedback'>Name must be longer than 3 characters</div> }
      </div>
      <div className="form-group">
        
        {/*after email input*/}
      { isValidEmail? null: <div className='invalid-feedback'>Email must be longer than 3 characters</div> }
      </div>
      <button type="submit" className="btn btn-success btn-block">
        Submit
      </button>
    </form>
  );
}

The form is validating instantly, and the error message will be gone when name is longer than 3 characters. But this validation isn't optimal because we are putting the validation logic into render method, which will turn the method into spaghetti real fast when we are validating lots of data. It also run even before we do anything with the textbox. That's not good.

Using state for error checking

Just as we used state for data entry, we can also use state for validation. We'll add new state property in our state initialization.

this.state = {
  name: '',
  email: '',
  nameError: '',
  emailError: ''
}

The formError state will keep our error message, and we'll use them for displaying any error message we might have. Let's put them into context by creating new validate functions:

handleNameChange = event => {
  this.setState({ name: event.target.value }, () => {
    this.validateName();
  });
};

handleEmailChange = event => {
  this.setState({ email: event.target.value }, () => {
    this.validateEmail();
  });
};

validateName = () => {
  const { name } = this.state;
  this.setState({
    nameError:
      name.length > 3 ? null : 'Name must be longer than 3 characters'
  });
}

validateEmail = () => {
  const { email } = this.state;
  this.setState({
    emailError:
      email.length > 3 ? null : 'Email must be longer than 3 characters'
  });
}

With this, only when user type something into the inputs will the validation method run. Now the last thing we have to do is running validation when user clicked on a textbox, and then move to click another textbox without doing anything.

Adding onBlur

Let's add an onBlur prop to our input elements.

<input
  name='name'
  // className, id, onChange ...
  onBlur={this.validateName}
/>

<input
  name='email'
  // className, id, onChange ...
  onBlur={this.validateEmail}
/>

Now the validation method will run on the corresponding texbox that was "touched" by users, and then display any error message if it has.

Here's the demo again:

Conclusion

Now it's time to wrap what we have learned from this simple example. Let's repeat the 3 basic principles of React form again:

  1. component is used for rendering form elements, normally JSX elements
  2. state is used to keep track of user's inputs
  3. props is used for passing data into JSX elements

We have seen how this is a tried and true principle of React form. We have written a component that renders our JSX form. We have used state to keep track of name and email value, and we have used props to pass data from state values into input values, including passing a handleChange function into onChange props.

Making form in React is quite a complex task for those still unfamiliar with the way React handles data. If you need some advanced guide on React form, I recommend you to checkout Arinich's high quality React form tutorials. It can help you save lots of time.

Thanks for reading! If you love articles like this, be sure to follow me. I'll be writing more tutorials on React soon.

Share this:
LinkedIn
Reddit
WhatsApp

Get Free Guides