« Previous Tutorial Next Tutorial »

Let’s talk about form validation. Since the earliest days of the web, people have been creating forms with which to gather data from users, and users have been putting data into those forms that’s incomplete, malformed, or otherwise not what the original developer was looking for. Form validation is an attempt to prevent, or at least mitigate, this problem. By ensuring that a user’s data is as close as possible to what’s expected, we can reduce the amount of junk data in our DB, which is valuable for a variety of reasons.

When most people hear the term “form validation,” they tend to think about front-end validation, and that’s where we’ll be focusing for the majority of this tutorial, but validation on the back-end is also important. For example, experienced developers who’ve been following this tutorial series have probably picked up on a potentially catastrophic problem with our registration API: right now there’s no check for duplicate emails, which is a very bad thing (Passport only checks usernames). Putting in this check is a type of form validation, and we’re going to add it, along with front-end stuff like “does this email address at least look like a valid email address?” and “did the user remember to enter their password?”

Let’s get started. We’re going to use a 3rd party module for front-end validation that’s built specifically to work with Reactstrap. It’s called Availity reactstrap Validation and we install it by heading for a terminal window or command prompt, switching to our musiclist directory, and typing:

yarn add availity-reactstrap-validation

With that done, switch back to Sublime (or your editor of choice) and open up /src/components/account/LoginPage.jsx. We’ll start with the simpler of our two forms. The first thing we need to do is adjust our imports, so change line 2 to these two lines:

import { AvForm, AvGroup, AvInput, AvFeedback } from 'availity-reactstrap-validation';
import { Button, Label } from 'reactstrap';

The various Av-based components are wrappers for reactstrap components that allow for validation, so we no longer need to import the reactstrap components (the availity components do that for us), except for Button and Label because those aren’t changing.

Next up, find your compileFormData method on line 39 and replace the whole thing with the following:

  // Handle submission once all form data is valid
  handleValidSubmit() {
    const { loginFunction } = this.props;
    const formData = this.state;
    loginFunction(formData);
  }

This is the same function, just re-named (and I added a comment for clarity). This also means we need to delete line 10, which binds our now non-existent compileFormData function. So nuke that line and then, below line 12, add the following:

    this.handleValidSubmit = this.handleValidSubmit.bind(this);

Now head down to our render block and change lines 50 and 51 from:

          <Form>
            <FormGroup>

to:

          <AvForm onValidSubmit={this.handleValidSubmit}>
            <AvGroup>

That AvForm line is especially important because by including our handleValidSubmit function, we tell it what to do when the validated form is submitted. Lines 53 to 61 make up our email input. We need to make a few changes here, including switching to AvInput, adding a required attribute, and organizing our attributes alphabetically. Here’s the entire code block:

              <AvInput
                id="userEmail"
                name="email"
                onChange={this.handleEmailChange}
                onKeyPress={this.handleKeyPress}
                placeholder="noreply@musiclist.com"
                required
                type="email"
                value={this.state.email}
              />

Super useful pro-tip that I wish I’d known about years earlier than I did you can sort lines alphabetically in Sublime by highlighting them and then just hitting F9. This is crazy useful when working in CSS, or when refactoring code in general.

Now below line 62, which closes our input, add the following:

              <AvFeedback>A valid email is required to log in.</AvFeedback>

Then make sure to change the &lt;/FormGroup&gt; just below that to:

            </AvGroup>

Now we need to repeat the process for our password input. Here’s all of the code:

            <AvGroup>
              <Label for="userPassword">Password</Label>
              <AvInput
                id="userPassword"
                name="password"
                onChange={this.handlePasswordChange}
                onKeyPress={this.handleKeyPress}
                placeholder="password"
                required
                type="password"
                value={this.state.password}
              />
              <AvFeedback>Password is required to log in</AvFeedback>
            </AvGroup>

Then change line 79, our Button line, to the following:

            <Button color="primary">Log In</Button>

We don’t need the OnClick anymore because AvForm will fire this.handleValidSubmit automatically. Last thing we need to do is change line 80 to:

          </AvForm>

That’s it for this file. Save it, and let’s head over to /src/components/account/RegisterPage.jsx. We’re doing basically the same set of changes here, just more of them. So change line 2 to the following two lines:

import { AvForm, AvGroup, AvInput, AvFeedback } from 'availity-reactstrap-validation';
import { Button, Label } from 'reactstrap';

Then delete the compileFormData method on lines 24 to 29, and below the end of the handleKeypress method on line 35, add the following:

  // Handle submission once all form data is valid
  handleValidSubmit() {
    const { registerFunction } = this.props;
    const formData = this.state;
    registerFunction(formData);
  }

Then go back up, delete line 10 (our binding for compileFormData) and below what is now line 11, add the following:

    this.handleValidSubmit = this.handleValidSubmit.bind(this);

Now on to our render block. I’m not going to step you through changing each field. My suggestion is to try doing them all yourself based on what you learned from changing LoginPage.jsx, and then watch the rest of this video where I will go through quickly and without explanation, and just change each field check the code below to make sure you made all the changes. Here’s the entire render block.

  render() {
    return (
      <div className="row justify-content-center">
        <div className="col-10 col-sm-7 col-md-5 col-lg-4">
          <p>
            Want to get started saving your favorite bands to MusicList?
            Create an account! All fields are required.
          </p>
          <AvForm onValidSubmit={this.handleValidSubmit}>
            <AvGroup>
              <Label for="email">Email</Label>
              <AvInput
                id="email"
                name="email"
                onChange={this.handleInputChange}
                onKeyPress={this.handleKeyPress}
                placeholder="noreply@musiclist.com"
                required
                type="email"
                value={this.state.email}
              />
              <AvFeedback>A valid email is required to register.</AvFeedback>
            </AvGroup>

            <AvGroup>
              <Label for="password">Password</Label>
              <AvInput
                id="password"
                minLength="8"
                name="password"
                onChange={this.handleInputChange}
                onKeyPress={this.handleKeyPress}
                placeholder="password"
                required
                type="password"
                value={this.state.password}
              />
              <AvFeedback>Passwords must be at least eight characters in length</AvFeedback>
              <span>
                We recommend a password service like 
                <a href="https://www.lastpass.com/" target="_blank" rel="noopener noreferrer">LastPass</a>
                 or <a href="https://1password.com/" target="_blank" rel="noopener
noreferrer">1Password</a>
              </span>
            </AvGroup>

            <AvGroup>
              <Label for="username">Username</Label>
              <AvInput
                id="username"
                name="username"
                onChange={this.handleInputChange}
                onKeyPress={this.handleKeyPress}
                placeholder="CaptainCode"
                required
                type="text"
                value={this.state.username}
              />
              <AvFeedback>A username is required to register</AvFeedback>
            </AvGroup>

            <AvGroup>
              <Label for="firstName">First Name</Label>
              <AvInput
                id="firstName"
                name="firstName"
                onChange={this.handleInputChange}
                onKeyPress={this.handleKeyPress}
                placeholder="Jamie"
                required
                type="text"
                value={this.state.firstName}
              />
              <AvFeedback>A first name is required to register</AvFeedback>
            </AvGroup>

            <AvGroup>
              <Label for="lastName">Last Name</Label>
              <AvInput
                id="lastName"
                name="lastName"
                onChange={this.handleInputChange}
                onKeyPress={this.handleKeyPress}
                placeholder="Smith"
                required
                type="text"
                value={this.state.lastName}
              />
              <AvFeedback>A last name is required to register</AvFeedback>
            </AvGroup>

            <Button color="primary">Register</Button>
          </AvForm>
        </div>
      </div>
    );
  }

As you can see, I added a little bit of text about fields being required, and also added a minimum length of eight characters to the password field. You can adjust that as you see fit.

All set? We’re good to go here, too. Save the file and head for a browser. Now we can see if our validation is working. Make sure you’re logged out, and then head first for our login page. Try submitting the form blank. Aha! No dice, which is what we want. You can also try with a malformed email address or a short password, and it should complain as well. But if you log in with correct credentials … all set.

Log out again and head for the registration form. Same deal, here. You can test your front-end validation in a variety of ways, and it should all work. We haven’t fixed the back-end problem with duplicate email addresses yet, though, so be careful there!

That’s it for simple front-end validation. In the next tutorial, we’ll fix the issue I just mentioned, and also do a little more cleanup when it comes to error reporting. See you there!

« Previous Tutorial Next Tutorial »