Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
The React Workshop

You're reading from   The React Workshop Get started with building web applications using practical tips and examples from React use cases

Arrow left icon
Product type Paperback
Published in Aug 2020
Publisher Packt
ISBN-13 9781838645564
Length 806 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (6):
Arrow left icon
Florian Sloot Florian Sloot
Author Profile Icon Florian Sloot
Florian Sloot
Ryan Yu Ryan Yu
Author Profile Icon Ryan Yu
Ryan Yu
Brandon Richey Brandon Richey
Author Profile Icon Brandon Richey
Brandon Richey
Endre Vegh Endre Vegh
Author Profile Icon Endre Vegh
Endre Vegh
 Theofanis Despoudis Theofanis Despoudis
Author Profile Icon Theofanis Despoudis
Theofanis Despoudis
Anton Punith Anton Punith
Author Profile Icon Anton Punith
Anton Punith
+2 more Show less
Arrow right icon
View More author details
Toc

Table of Contents (20) Chapters Close

Preface
1. Getting Started with React 2. Dealing with React Events FREE CHAPTER 3. Conditional Rendering and for Loops 4. React Lifecycle Methods 5. Class and Function Components 6. State and Props 7. Communication between Components 8. Introduction to Formik 9. Introduction to React Router 10. Advanced Routing Techniques: Special Cases 11. Hooks – Reusability, Readability, and a Different Mental Model 12. State Management with Hooks 13. Composing Hooks to Solve Complex Problems 14. Fetching Data by Making API Requests 15. Promise API and async/await 16. Fetching Data on Initial Render and Refactoring with Hooks 17. Refs in React 18. Practical Use Cases of Refs Appendix

Context of Event Handlers

While building a form, you will be interacting very heavily with event handlers, and understanding the context of this property will prevent you from scratching your head down the line when you get error messages such as this is undefined. Especially in the case of forms, you will be dealing very heavily with event handlers and event handlers will change the context of this property. Now, if we want to set the new state in our baseline component of the form we created earlier, we will have to call out this.setState in our validateUsernameOnBlur function. However, if you try to do that, you are going to hit a this is undefined error message. For example, take changing our validateUsernameOnBlur function to the following:

validateUsernameOnBlur(event) {
  console.log("I should validate whatever is in ", event.target.value);
  this.setState();
}

The preceding code results in the following error:

Figure 2.10: TypeError in the console statement

Figure 2.10: TypeError in the console statement

The reason is that because the event handler code is essentially wrapping up the call and calling our event handler function for us, it's losing the context of the function, which should essentially be the component. Instead, the context becomes undefined. The this is undefined error message can be hard to track down if you don't know what you are looking for. The error message is ambiguous as this keyword is not explained.

The good news is that this is an incredibly simple thing to solve. We can explicitly tell JavaScript to bind the context of this keyword to the component itself instead of allowing the event handler to show us the context. There are two common ways to address this:

  • In-line bind statements
  • Constructor bind statements

In-line Bind Statements

The simplest method to add a context to our baseline component is to add bind(this) call to the end of our event handler declaration in the input field like so:

displayForm() {
  return (
    <div>
      Username: <input       type="text" onBlur={this.validateUsernameOnBlur.bind(this)} /><br />
      Password: <input type="text" /><br />
      Password Confirmation: <input type="text" /><br />
      Email: <input type="text" /><br />
      <br />
      <button onClick={this.submitForm}>Submit</button>
    </div>
  );
}

Make that change and the code will start working again when you select a field other than the username field. This is a shortcut to solve this problem if you only need the bind in a single place, but is not a great strategy if we have to write in-line bind statements multiple times in the code, especially for repeat calls to the same functions. We will see another way to bind this concept to the component next.

Constructor Bind Statements

We use constructor bind statements to tell JavaScript explicitly that when we reference the this.validateUsernameOnBlur function in our component, it should always have the context of the component bound to it when this is referenced.

Since the constructor in the class-based components is used to declare the state of a component, when we are calling this.state() in the constructor, we should bind our event handlers explicitly inside of our constructor to avoid doing repetitive tasks, especially for the same functions, to save ourselves a little bit of extra time and effort. This requires us to add the following line to the constructor:

this.validateUsernameOnBlur = this.validateUsernameOnBlur.bind(this);

Note

Constructors have been discussed in detail in Chapter 4, React Lifecycle Methods.

Now we can go back to our displayForm() function and remove the in-line bind statement instead:

displayForm() {
  return (
    <div>
      Username: <input type="text" onBlur={this.validateUsernameOnBlur} /><br />
      Password: <input type="text" /><br />
      Password Confirmation: <input type="text" /><br />
      Email: <input type="text" /><br />
      <br />
      <button onClick={this.submitForm}>Submit</button>
    </div>
  );
}

Our code will otherwise remain identical. If you try this again, you should again see the focus change work and not result in any additional errors. Let's practice this in the following exercise.

Exercise 2.03: Building our Username Validator

In this exercise, we will put the code we just talked about into our component that we created previously. We will add the bind statement to the constructor and call our validateUsernameOnBlur function from our displayForm function when the form input hits the onBlur event. To do so, let's go through the following steps:

  1. Drop our constructor since we can specify the initial state in a different way. Instead, we will use JavaScript fields to define the state by setting a state field on the class:
    class App extends Component {
      state = {
        username: '',
        password: '',
        passwordConfirmation: '',
        email: '',
        errors: []
      };
      validateUsernameOnBlur = this.validateUsernameOnBlur.bind(this);
  2. Write the validateUsernameOnBlur() function. Nothing here should be new:
    validateUsernameOnBlur(event) {
      console.log("I should validate whatever is in ", event.target.value);
      this.setState();
    }
  3. Call the onBlur event handler inside the displayForm() function, without needing an in-line bind statement:
    displayForm() {
      return (
        <div>
          Username: <input type="text" onBlur={this.validateUsernameOnBlur} /><br />
          Password: <input type="text" /><br />
          Password Confirmation: <input type="text" /><br />
          Email: <input type="text" /><br />
          <br />
          <button onClick={this.submitForm}>Submit</button>
        </div>
      );
    }
  4. Call the submitForm function, which is active while the Submit button is pressed:
    submitForm(event) {
          console.log("Submitting the form now...");
          console.log(event);
        }
  5. Call displayForm from the render method:
    render() {
          return (
            <div className="App">
              Create Account
              <hr />
              {this.displayForm()}
            </div>
          )
        }

    The resulting class structure looks like the following:

App.js

3  class App extends Component {
4    state = {
5      username: '',
6      password: '',
7      passwordConfirmation: '',
8      email: '',
9      errors: []

The output is as follows:

Figure 2.11: Form component

Figure 2.11: Form component

Another way to define our components is to use some relatively newer syntax (the public field syntax) to define our class component, the properties in the component, and the functions in the component. This allows us to define our functions in such a way that they remember the binding of this keyword regardless of how they are passed or called via event handlers.

Exercise 2.04: Using Alternative Class Declarations to Avoid Binds

In this exercise, we will use an alternative class declaration so that we can avoid the bind statements altogether. We will use the displayForm component that we created previously. We will drop the constructor and we will see how to specify the initial state in a different way using arrow functions and declaring the fields inside it. To do so, let's go through the following steps:

  1. Drop our constructor since we can specify the initial state in a different way. Instead, we will use JavaScript fields to define the state by setting a state field on the class:
    class App extends Component {
      state = {
        username: '',
        password: '',
        passwordConfirmation: '',
        email: '',
        errors: []
      };
    }
  2. Redefine the validateUsernameOnBlur() function by changing the function to instead be a field on the class as well. You will need to use the arrow function syntax, mentioned in Chapter 1, Getting Started with React, to make this work:
    validateUsernameOnBlur = (event) => {
      console.log("I should validate whatever is in ", event.target.value);
      this.setState();
    }

    The only major difference here is that we are defining the function in a similar way to how we define other arrow functions. The advantage here is that this function now has this bound appropriately, so we don't need to worry about explicitly binding.

  3. Call the onBlur event handler inside the displayForm() function:
    displayForm() {
          return (
            <div>
              Username: <input type="text" onBlur={this.validateUsernameOnBlur}         /><br />
              Password: <input type="text" /><br />
              Password Confirmation: <input type="text" /><br />
              Email: <input type="text" /><br />
              <br />
              <button onClick={this.submitForm}>Submit</button>
            </div>
          );
        }
  4. Call the submitForm function, which is active while the Submit button is pressed:
    submitForm(event) {
          console.log("Submitting the form now...");
          console.log(event);
        }
  5. Call displayForm from the render method:
    render() {
          return (
            <div className="App">
              Create Account
              <hr />
              {this.displayForm()}
            </div>
          )
        }

    The resulting class structure looks like the following:

App.js

1  class App extends Component {
2    this.state = {
3      username: '',
4      password: '',
5      passwordConfirmation: '',
6      email: '',
7      errors: []
8    };

The output is as follows:

Figure 2.12: Validation in the form component

Figure 2.12: Validation in the form component

Unfortunately, this is still a syntax proposal and is not guaranteed to be supported in every environment that you may be working in, so for now, we will stick with the more widely available syntax. If you are working in a Create React App project, though, and feel more comfortable using the proposed fields syntax instead, that remains an option. Create React App will create a project with the appropriate Babel config to use the public class fields syntax by default.

Now let's look at the ways to handle our validation, but we are going to go with an approach that is more aligned with common React best practices.

You have been reading a chapter from
The React Workshop
Published in: Aug 2020
Publisher: Packt
ISBN-13: 9781838645564
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image