Book Image

The React Workshop

By : Brandon Richey, Ryan Yu, Endre Vegh, Theofanis Despoudis, Anton Punith, Florian Sloot
5 (1)
Book Image

The React Workshop

5 (1)
By: Brandon Richey, Ryan Yu, Endre Vegh, Theofanis Despoudis, Anton Punith, Florian Sloot

Overview of this book

Are you interested in how React takes command of the view layer for web and mobile apps and changes the data of large web applications without needing to reload the page? This workshop will help you learn how and show you how to develop and enhance web apps using the features of the React framework with interesting examples and exercises. The workshop starts by demonstrating how to create your first React project. You’ll tap into React’s popular feature JSX to develop templates and use DOM events to make your project interactive. Next, you’ll focus on the lifecycle of the React component and understand how components are created, mounted, unmounted, and destroyed. Later, you’ll create and customize components to understand the data flow in React and how props and state communicate between components. You’ll also use Formik to create forms in React to explore the concept of controlled and uncontrolled components and even play with React Router to navigate between React components. The chapters that follow will help you build an interesting image-search app to fetch data from the outside world and populate the data to the React app. Finally, you’ll understand what ref API is and how it is used to manipulate DOM in an imperative way. By the end of this React book, you’ll have the skills you need to set up and create web apps using React.
Table of Contents (20 chapters)
Preface

Form Validation in React

We need to finish creating the validation check to make sure the username is not empty when the mouse cursor moves away from the field. We will need to do a few things to achieve this functionality:

We will need to build a rendering function that displays the list of form errors. We will need to hook the validateUsernameOnBlur() function to validate the username and update the state where appropriate.

The first task is simple enough that we should be able to build it without really needing to update much else, so we will start there.

Exercise 2.05: Creating a Validation for Input Fields

We are still working with the same form that we have been working with this entire chapter. We will continue to iterate on this form now by adding a little more validation for the username field. The list of errors that we have is a relatively simple thing to display; all we need to do is iterate over the list of errors, and then for each of those, we need to just display a simple quick string. React has a little gotcha that we will run into when building out lists of elements dynamically: each individual item requires a separate entry for the key of the item. This allows React to quickly identify each item uniquely and update the DOM representing that item when it changes:

  1. Write a displayErrors() function that, given a list of errors from our state, maps over each. You will need to set the key property and determine some way to uniquely identify each element as a value for the key property:
    displayErrors() {
       return (
         <div className="errors">
           {this.state.errors.map((err, i) => <p key={`err-${i}`}>{err}</p>)}
         </div>
       );
     }

    We start off with a simple-enough function signature; there are no arguments that we are passing along. Instead, we just have a simple map call where we are passing each error and the index of the iteration. This allows us to set a unique key for each item, which we then render with a simple <p> tag.

  2. Add the displayErrors() call to our main render function:
    render() {
      return (
        <div className="App">
          Create Account
          {this.displayErrors()}
          <hr />
          {this.displayForm()}
        </div>
      )
     }
  3. Recreate src/App.css and add a single bit of CSS to it:
    .errors {
      color: red;
    }
  4. Change our src/App.js file to import this CSS file near the top:
    import "./App.css";

    Since we don't want to write multiple validations every single time for each field that needs to be validated to ensure that it's not blank, we will start off by refactoring our code to move the not-empty check into a new function.

  5. Create a new function to validate whether the field is blank or not:
    validateNotEmpty(fieldName, value) {
      if (value.length <= 0) {
        return `${fieldName} must be filled out.`;
      }
     }

    We will want to include not just the value that we need to validate, but also the field name to be able to generate the appropriate error message. We check the value supplied to make sure it's not blank, and then if it is, we will return a string back with the appropriate error message.

  6. Modify our validateUsernameOnBlur() function call from Exercise 2.04, Using Alternative Class Declarations to Avoid Binds to a new helper function to perform the validation:
    validateUsernameOnBlur(event) {
      const username = event.target.value;
      const errors = this.state.errors;
      errors.push(this.validateNotEmpty("Username", username));
      this.setState({ username, errors });
    }

    The bulk of the function stays the same, but now writing a validatePasswordOnBlur function becomes significantly easier for us.

  7. Copy our validateUsernameOnBlur from the previous step and change username where appropriate to password:
    validatePasswordOnBlur(event) {
      const password = event.target.value;
      const errors = this.state.errors;
      errors.push(this.validateNotEmpty("Password", password));
      this.setState({ password, errors });
    }
  8. We will add the constructor again in this step. Use the bind statement in the constructor:
    constructor(props) {
      super(props);
      this.state = {
        username: '',
        password: '',
        passwordConfirmation: '',
        email: '',
        errors: []
      };
      this.validateUsernameOnBlur = this.validateUsernameOnBlur.bind(this);
      this.validatePasswordOnBlur = this.validatePasswordOnBlur.bind(this);
    }
  9. Change our render function as well to modify the password field to use this new validation function:
    displayForm() {
      return (
        <div>
          Username: <input type="text" onBlur={this.validateUsernameOnBlur}        /><br />
          Password: <input type="text" onBlur={this.validatePasswordOnBlur}       /><br />
          Password Confirmation: <input type="text" /><br />
          Email: <input type="text" /><br />
          <br />
          <button onClick={this.submitForm}>Submit</button>
        </div>
      );
    }
  10. Write our validateEmailOnBlur() function. The code in validateEmailOnBlur() is simple enough and follows the same format we have been using:
    validateEmailOnBlur(event) {
      const email = event.target.value;
      const errors = this.state.errors;
      errors.push(this.validateEmailFormat("Email", email));
      this.setState({ email, errors });
    }
  11. Split the field's value on a @ character and verify that both sides have at least one character in them:
    validateEmailFormat(fieldName, value) {
      let [lhs, rhs] = value.split('@');
      lhs = lhs || '';
      rhs = rhs || '';
      if (lhs.length <= 0 || rhs.length <= 0) {
        return `${fieldName} must be in a standard email format.`;
      }
    }
  12. Modify the email text field in the displayForm() function:
    Email: <input type="text" onBlur={this.validateEmailOnBlur} /><br />
  13. Add validations for our password confirmation to ensure it matches the password by writing a validatePasswordConfirmationOnBlur() function, adding it to render, and adding the binds for the last two validation functions we wrote. First, let's write the validatePasswordConfirmationOnBlur() function:
    validatePasswordConfirmationOnBlur(event) {
      const passwordConfirmation = event.target.value;
      const errors = this.state.errors;
      if (passwordConfirmation !== this.state.password) {
        errors.push("Password must match password confirmation.");
      }
      this.setState({ passwordConfirmation, errors });
    }
  14. Add the call to our new function for displayForm() to our password confirmation field:
    Password Confirmation: <input type="text" onBlur={this.validatePasswordConfirmationOnBlur} /><br />
  15. Finally, make sure all these functions are appropriately bound in our constructor (we have added two of these already, but we will include all four for the sake of completeness):
    this.validateUsernameOnBlur = this.validateUsernameOnBlur.bind(this);
    this.validatePasswordOnBlur = this.validatePasswordOnBlur.bind(this);
    this.validatePasswordConfirmationOnBlur =      this.validatePasswordConfirmationOnBlur.bind(this);
    this.validateEmailOnBlur = this.validateEmailOnBlur.bind(this);

    Now, when you run through the form and break all the rules we have established here, you should see all of the error messages show up at the top of the form:

    Figure 2.13: Form component with the error messages

Figure 2.13: Form component with the error messages

The great news is that any other event handlers you may want to write along the way are going to hinge on the same rules that you have just learned about here. It is important to become comfortable with writing event listeners and with knowing how and when to bind functions to avoid weird issues as you write more complicated code.

Now that we have explored how to build React event handlers and to dynamically modify what we are rendering; we need to put it into practice the concepts we have learned so far.

Activity 2.01: Create a Blog Post Using React Event Handlers

In this activity, we are going to build out an application that keeps track of the number of characters entered into a text area. This will require us to hook into a new event that we have not used yet and change the text and element rendered to the page to display the total length of the field. Specifically, we are going to make a text area that, unless the user has entered at least 100 characters, will not allow you to submit the form and post the text.

Here, we will build a new React application with Create React App and then build a text area, adding the length of the field next to it:

Figure 2.14: Blog Post

Figure 2.14: Blog Post

The following steps will help to complete the activity:

  1. Create your project, called fieldlength, via the Create React App CLI.
  2. Delete all the unnecessary files for our project.
  3. Build the App React component as a class component but leave it blank.
  4. Give the component an initial state with a single element in the state; this will store the input from the textarea.
  5. Add a textarea to the component.
  6. Add a function that will act as the event handler. This function will need to accept an event as an argument and should update the state of the component by setting the input from the textarea. Add this to the textarea.
  7. The event handler you need to use here is up to you; if you need a hint, look for one that can react to changes to your form or input to the form.
  8. Add a function that will return N characters, wrapped inside JSX, where N is the length of the input in the textarea.
  9. Add the function above to the display of your component.
  10. Add a submit button to make the app look like a blog post editor.
  11. Include some instructions for the user that the post must be 100 characters.
  12. Write a validation function to monitor the length of the field.

    Hint: Try to use a callback with setState for the best results.

  13. Create an alert box to visually notify the user before submitting a blog post.
  14. Verify that as you type text into the textarea; the display is now updated.

    Note

    The solution for this activity can be found on page 609.