React Tutorial Part 25: Workshop on Forms
- Transfer
- Tutorial
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
App
that is in the file of the App.js
standard 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
age
in 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 dietaryRestrictions
initialized 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.
name
so 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 value
whose 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
onChange
yet , 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.target
properties from the object name
and value
then 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
this
performed 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
, secondName
and 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 age
in 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
age
with 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
age
prompt 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
checked
and 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 switcheschecked
set 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 onChange
assign 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
checked
be issued true
. After clicking on one of them, the corresponding value (stored in the attribute of the value
switch) 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
onChange
this.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
dietaryRestrictions
that 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 checked
view constructs are used as the values of their attributes this.state.dietaryRestrictions.isSomething
. Please note that although
onChange
we 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 type
and 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
handleChange
to the following form. Here we proceed from the assumption that the properties of an object dietaryRestrictions
can 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 dietaryRestrictions
state 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?