React Tutorial Part 25: Workshop on Forms

Original author: Bob Ziroll
  • Transfer
  • Tutorial
In today's part of the translation of the React training course, you are invited to complete a form task. → Part 1: course overview, reasons for the popularity of React, ReactDOM and JSXPart 2: functional componentsPart 3: component files, project structurePart 4: parent and child componentsPart 5: start of work on a TODO application, basics of stylingPart 6: about some features of the course, JSX and JavaScriptPart 7: built-in stylesPart 8: continued work on the TODO application, familiarity with the properties of componentsPart 9: properties of components

image










Part 10: workshop on working with component properties and stylization
Part 11: dynamic markup generation and map arrays method
Part 12: workshop, third stage of work on a TODO application
Part 13: class-based components
Part 14: workshop on class-based components, state of components
Part 15: workshops on working with the state of components
Part 16: fourth stage of work on a TODO application, event handling
Part 17: fifth stage of work on TODO application, modification of state of components
Part 18 : sha that phase of work on TODO-application
Part 19: component life cycle methods
Part 20: first lesson on conditional rendering
Part 21: second lesson and workshop on conditional rendering
Part 22: seventh stage of work on a TODO application, downloading data from external sources
Part 23: first lesson on working with forms
Part 24: second lesson on working with forms
Part 25: workshop on working with forms
Part 26: application architecture, Pattern Container / Component
Part 27: course project

Lesson 43. Workshop. Work with forms


Original

▍Job


In this practical lesson, you are invited to bring the component code Appthat is in the file of the App.jsstandard project created by the create-react-app tools to a healthy state . Here is the code:

import React, {Component} from "react"
class App extends Component {
    constructor() {
        super()
        this.state = {}
    }
    render() {
        return (
            
               
                   
                   
                   
                   {/* Здесь создайте переключатели для выбора пола */}                    
                   {/* Здесь создайте поле со списком для выбора пункта назначения */}                    
                   {/* Здесь создайте флажки для указания диетологических ограничений */}                    
                                   
               
               

Entered information:

               

Your name: {/* Имя и фамилия */}

               

Your age: {/* Возраст */}

               

Your gender: {/* Пол */}

               

Your destination: {/* Пункт назначения */}

               

                   Your dietary restrictions:                    {/* Список диетологических ограничений */}                

           
       )    } } export default App

In general, your task is to ensure that the data that the user enters while working with the form controls immediately appears in the text below this form. Take advantage of managed component technology as you complete the task . It should be noted that the task offered to you is an adapted version of this task, so you can take a look at it in order to better understand the features of the controls that you are invited to create and configure.

Here is what the component now displays on the screen.


Application in browser

▍Solution


You can approach the solution of the problem proposed to you from different angles. We will start by putting everything we need into the state, after which we will set up the controls and other mechanisms of the component.

At the moment, the state of the component will look as shown below.

this.state = {
    firstName: "",
    lastName: "",
    age: 0,
    gender: "",
    destination: "",
    dietaryRestrictions: []
}

It is necessary to take into account the fact that in the process of working on a program it may turn out that, for example, it will be more convenient to initialize the state differently. If we run into something similar, we will change the state initialization code. In particular, now some doubts may be caused by the number 0 written in a property agein which it is supposed to store the age entered by the user. Perhaps, it will be necessary to do otherwise with the flag data storage system, which is now represented by a property dietaryRestrictionsinitialized by an empty array.

Now, after the initialization of the state, let’s take care of setting up the controls. Since the code already has a description of the input fields - let's get started with them.

These controls will need to be given names by setting their attributes.nameso that they coincide with the names of the state properties in which the data entered in these fields will be stored. They must have an attribute valuewhose value is determined based on data stored in the state. When entering data into each of these fields, you need to pass the entered data to the component, which leads to the need for them to have an event handler onChange. All these considerations lead to the fact that the description of the fields now looks like this:




As onChangeyet , the method used to process the events of these fields is indicated as non-existent this.handleChange. Create this method:

handleChange(event) {
    const {name, value} = event.target
    this.setState({
        [name]: value
    })
}

Here we extract the event.targetproperties from the object nameand valuethen use them to set the corresponding state property. At the moment, such a universal event handler code will suit us, but later, when we get to working with flags, we will make changes to it.

Do not forget about the binding thisperformed in the component constructor:

this.handleChange = this.handleChange.bind(this)

In order to achieve output at the bottom of the page of data entered into the fields firstName, secondNameand age, we will work with the corresponding elements

, bringing them to the following form:

Your name: {this.state.firstName} {this.state.lastName}

Your age: {this.state.age}


Now let's take a look at what we got.


Application in the browser

As you can see, in the field designed to enter the age, a hint is not displayed. Instead, what is set in the state property is displayed age, that is, 0. We also need a hint in the empty field. Let's try to replace the value agein the state with null. After that, it turns out that the form looks as it should, but the following warning is displayed in the console regarding the field age:

Warning: `value` prop on `input` should not be null. Consider using an empty string to clear the component or `undefined` for uncontrolled components

As a result, we will need to replace the value of the state property agewith an empty string, bringing the state initialization code to the following form:

this.state = {
    firstName: "",
    lastName: "",
    age: "",
    gender: "",
    destination: "",
    dietaryRestrictions: []
}

Now try the form. Immediately after the opening, it will look the same as at the very beginning of the work, that is, the ageprompt will return to the field . When filling in the fields, the entered data will be displayed at the bottom of the page.


The application in the browser

As you can see, at this stage of work, everything functions as expected.

Now we will be engaged in new elements. The next step in working on the form will be adding switches to it.

We wrap the switches in a tag , which will allow us not only to sign the switch, but also to make sure that clicking on this signature, that is, on its parent element, leads to its selection.

When working with switches, it’s worth remembering that they are something like a combination of checkboxes that have an attribute checkedand text fields that have an attribute value. The switches form a group in which each of the switches is assigned the same name, with the property of the switchescheckedset according to a condition that is configured so that it would be impossible to turn on more than one switch included in the same group. We will onChangeassign switches as an event handler this.handleChange.

As a result, the switch description code will look like this:



Now we will process the corresponding element

located at the bottom of the page as follows:

Your gender: {this.state.gender}


After that, the form can be tested. Immediately after starting, both switches are unselected, since a state is stored in a state that does not allow any of the checks performed when setting their properties to checkedbe issued true. After clicking on one of them, the corresponding value (stored in the attribute of the valueswitch) enters the state , the switch is selected, the corresponding text is displayed at the bottom of the form.


Application in the browser

Now let's work on the combo box. His workpiece looks like this:


This code shows that we plan to describe a combo box containing four items.

The tag If we talk about the tag attribute Assign the field other attributes. In particular, the name corresponding to the name of the property in the state, and the event handler - .                
value                
onChangethis.handleChange


Now set up the item description

, which will display what is selected in the field destination:

Your destination: {this.state.destination}


If you look at the page in the browser right now, you can see that the first element of the list is automatically selected in the field, but this obviously does not lead to an update of the state, since Your destination:nothing is displayed in the line after the colon.


Application in the browser

In order for the value to fall into the state germany, you need to open the combo box and first select something else, and then the item Germany.

Often, in order to take into account this feature of list fields, in their lists, as the first element, they put something like an element with an empty value and with text like -- Please Choose a destination --. In our case, it may look like this:


We will focus on this option of setting the combo box.

Now let's deal with, perhaps, the most difficult part of our task, which is associated with flags. Here it is worth explaining that the state property dietaryRestrictionsthat is planned to be used to work with flags was initialized with an empty array. Now, when it comes to working with controls, there is a feeling that it would be better to represent this field as an object. So it will be more convenient to work with entities that represent individual flags in the form of properties of this object with friendly names, and not in the form of array elements. The properties of the object, which will now be represented by the state property dietaryRestrictions, will contain boolean values ​​that indicate whether the corresponding checkbox is cleared ( false) or checked (true) Now the state initialization code will look like this:

this.state = {
    firstName: "",
    lastName: "",
    age: "",
    gender: "",
    destination: "",
    dietaryRestrictions: {
        isVegan: false,
        isKosher: false,
        isLactoseFree: false
    }
}

As you can see, we plan to create three flags. All of them, immediately after loading the page, will be reset.

We describe the flags in the code returned by the component, wrapping them in tags and setting their attributes. Here is what their code will look like:




The names of the properties of the object are used as flag names dietaryRestrictions, and the checkedview constructs are used as the values ​​of their attributes this.state.dietaryRestrictions.isSomething.

Please note that although onChangewe already have a handler specified as a flag event handler this.handleChange, we must make some changes to the program to ensure the correct operation of the program.

Take a look at the application.


The application in the browser

As you can see, the flags on the page are displayed, but the component does not yet contain all the mechanisms necessary to ensure their proper operation. Let's deal with the event handler.

Here, to work with flags, we need to extract from the object event.target, in addition to the already extracted, properties typeand checked. The first is needed to check the type of the element (the flags are of the type represented by the string checkbox), the second is to find out if the checkbox is checked or unchecked. If it turns out that the handler was called after the user interacted with the flag, we use a special state setting procedure. We will handle the events of other controls in the same way as before.

When updating a state, it should be borne in mind that React is a fairly intelligent system, which, if only part of the state is updated, will automatically combine in the new state what has remained unchanged with what has changed. But one cannot be sure that the work with the properties of objects, which are the values ​​of state properties, will be carried out in the same way. We will verify this by bringing the code handleChangeto the following form. Here we proceed from the assumption that the properties of an object dietaryRestrictionscan be changed one at a time:

handleChange(event) {
    const {name, value, type, checked} = event.target
    type === "checkbox" ? 
        this.setState({
            dietaryRestrictions: {
                [name]: checked
            }
        })
    :
    this.setState({
        [name]: value
    }) 
}

If you open the application page in a browser, then immediately after downloading it, everything will look fine, if you try, for example, to enter a name in the field First Name, everything will also work as before, but when you try to install one of the flags, the following warning will be issued:
Warning: A component is changing a controlled input of type checkbox to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: fb.me/react-controlled-components

In order to correctly update the contents of the object dietaryRestrictions, you can use the functional form setState, having independently generated a new version of the state. If we would have to manage a large number of flags, then probably we would have done so. But here we will do otherwise. Namely, we make the properties of the object dietaryRestrictionsstate properties, getting rid of this object:

this.state = {
    firstName: "",
    lastName: "",
    age: "",
    gender: "",
    destination: "",
    isVegan: false,
    isKosher: false,
    isLactoseFree: false
}

Now we’ll change the settings for the attributes of the flags, getting rid of dietaryRestrictions:




And finally, edit the code of the element that displays information about the dietary restrictions specified by the user:

Your dietary restrictions:

Vegan: {this.state.isVegan ? "Yes" : "No"}

Kosher: {this.state.isKosher ? "Yes" : "No"}

Lactose Free: {this.state.isLactoseFree ? "Yes" : "No"}


After that, we’ll check the health of the application.


The application in the browser

As you can see, everything works as expected.

Here is the complete component code App:

import React, {Component} from "react"
class App extends Component {
    constructor() {
        super()
        this.state = {
            firstName: "",
            lastName: "",
            age: "",
            gender: "",
            destination: "",
            isVegan: false,
            isKosher: false,
            isLactoseFree: false
        }
        this.handleChange = this.handleChange.bind(this)
    }
    handleChange(event) {
        const {name, value, type, checked} = event.target
        type === "checkbox" ? 
            this.setState({
                [name]: checked
            })
        :
        this.setState({
            [name]: value
        }) 
    }
    render() {
        return (
            
               
                                       
                                       
                                       
                                       
                                       
                                       
                                       
                                       
                                       
                                   
               
               

Entered information:

               

Your name: {this.state.firstName} {this.state.lastName}

               

Your age: {this.state.age}

               

Your gender: {this.state.gender}

               

Your destination: {this.state.destination}

               

Your dietary restrictions:

               

Vegan: {this.state.isVegan ? "Yes" : "No"}

               

Kosher: {this.state.isKosher ? "Yes" : "No"}

               

Lactose Free: {this.state.isLactoseFree ? "Yes" : "No"}

           
       )    } } export default App

Summary


Today you have completed the practical work on the forms. Then you repeated what you learned in previous classes, and we hope you learned something new. Next time we'll talk about the architecture of React applications.

Dear readers! Tell me, was it difficult to complete this practical work?


Also popular now: