- PVSM.RU - https://www.pvsm.ru -

Учебный курс по React, часть 23: первое занятие по работе с формами

В этой части перевода учебного курса по React мы поговорим о работе с формами. В частности, сегодняшнее занятие посвящено организации взаимодействия компонентов и текстовых полей.

image [1]

Часть 1: обзор курса, причины популярности React, ReactDOM и JSX [2]
Часть 2: функциональные компоненты [3]
Часть 3: файлы компонентов, структура проектов [4]
Часть 4: родительские и дочерние компоненты [5]
Часть 5: начало работы над TODO-приложением, основы стилизации [6]
Часть 6: о некоторых особенностях курса, JSX и JavaScript [7]
Часть 7: встроенные стили [8]
Часть 8: продолжение работы над TODO-приложением, знакомство со свойствами компонентов [9]
Часть 9: свойства компонентов [10]
Часть 10: практикум по работе со свойствами компонентов и стилизации [11]
Часть 11: динамическое формирование разметки и метод массивов map [12]
Часть 12: практикум, третий этап работы над TODO-приложением [13]
Часть 13: компоненты, основанные на классах [14]
Часть 14: практикум по компонентам, основанным на классах, состояние компонентов [15]
Часть 15: практикумы по работе с состоянием компонентов [16]
Часть 16: четвёртый этап работы над TODO-приложением, обработка событий [17]
Часть 17: пятый этап работы над TODO-приложением, модификация состояния компонентов [18]
Часть 18: шестой этап работы над TODO-приложением [19]
Часть 19: методы жизненного цикла компонентов [20]
Часть 20: первое занятие по условному рендерингу [21]
Часть 21: второе занятие и практикум по условному рендерингу [22]
Часть 22: седьмой этап работы над TODO-приложением, загрузка данных из внешних источников [23]
Часть 23: первое занятие по работе с формами [1]

Занятие 41. Работа с формами, часть 1

Оригинал [24]

Формы являются достаточно важной частью веб-приложений. Но, как оказалось, у тех, кто занимается освоением React, работа с формами обычно вызывает определённые сложности. Дело в том, что в React с формами работают по-особенному. На этом занятии мы будем пользоваться стандартным проектом, создаваемым create-react-app, исходный вид файла App.js которого представлен ниже.

import React, {Component} from "react"

class App extends Component {
    constructor() {
        super()
        this.state = {}
    }
    
    render() {
        return (
            <div>
                Code goes here
            </div>
        )
    }
}

export default App

Обратите внимание на то, что для того, чтобы освоить материал этого занятия, вы должны быть знакомы с понятием состояния приложения. Если вы проработали все предыдущие занятия курса и самостоятельно выполняли практикумы, это значит, что вы обладаете знаниями, которые вам здесь понадобятся. Вот [25] документация React, посвящённая формам. Рекомендуется, прежде чем продолжать, взглянуть на неё.

Итак, в React с формами работают немного не так, как при использовании обычного JavaScript. А именно, при обычном подходе формы описывают средствами HTML на веб страницах, после чего, пользуясь API DOM, взаимодействуют с ними из JavaScript. В частности, по нажатию на кнопку отправки формы, собирают данные из полей, заполненных пользователем, и готовят их к отправке на сервер, проверяя их, и, при необходимости, сообщая пользователю о том, что он заполнил какие-то поля неверно. В React же, вместо того, чтобы ожидать ввода всех материалов в поля формы перед тем, как приступить к их программной обработке, за данными наблюдают постоянно, пользуясь состоянием приложения. Это, например, сводится к тому, что каждый символ, введённый пользователем с клавиатуры, сразу же попадает в состояние. В результате в React-приложении мы можем оперативно работать с самой свежей версией того, что пользователь вводит в поля формы. Для того, чтобы продемонстрировать эту идею в действии, начнём с описания формы, содержащей обычное текстовое поле.

Для этого, в коде, который возвращает метод render(), опишем форму. При обычном подходе такая форма имела бы кнопку, по нажатию на которую программные механизмы приложения приступают к обработке данных, введённых в эту форму. В нашем же случае данные, введённые пользователем в поле, будут поступать в приложение по мере их ввода. Для этого нам понадобится обрабатывать событие поля onChange. В обработчике этого события (назовём его handleChange()) мы будем обновлять состояние, записывая в него то, что введено в поле. Для этого нам понадобится, во-первых, узнать, что содержится в поле, и во-вторых — поместить эти данные в состояние. В состоянии создадим свойство, хранящее содержимое поля. Это поле мы собираемся использовать для хранения имени (first name) пользователя, поэтому назовём соответствующее свойство firstName и инициализируем его пустой строкой.

После этого, в конструкторе, привяжем обработчик события handleChange() к this и в коде обработчика воспользуемся функцией setState(). Так как предыдущее значение, которое хранилось в свойстве состояния firstName, нас не интересует, мы можем просто передать этой функции объект с новым значением firstName. Что должно быть записано в это свойство?

Если вспомнить то, как работают обработчики событий в JavaScript, то окажется, что при их вызове им передаются некие предопределённые параметры. В нашем случае обработчику передаётся объект события (event). Он и содержит интересующие нас данные. Текстовое поле, событие onChange которого мы обрабатываем, представлен в этом объекте в виде event.target. А к содержимому этого поля можно обратиться, воспользовавшись конструкцией event.target.value.

Теперь, в методе render(), выведем то, что будет храниться в состоянии и посмотрим на то, что у нас получилось.

Вот код, реализующий вышеописанные идеи.

import React, {Component} from "react"

class App extends Component {
    constructor() {
        super()
        this.state = {
            firstName: ""
        }
        this.handleChange = this.handleChange.bind(this)
    }
    
    handleChange(event) {
        this.setState({
            firstName: event.target.value
        })
    }
    
    render() {
        return (
            <form>
                <input type="text" placeholder="First Name" onChange={this.handleChange} />
                <h1>{this.state.firstName}</h1>
            </form>
        )
    }
}

export default App

Вот как всё это выглядит в браузере.

Учебный курс по React, часть 23: первое занятие по работе с формами - 2

Страница приложения в браузере

Каждый символ, введённый в поле, тут же появляется в элементе <h1>, присутствующем на странице.

Подумаем теперь о том, как добавить на форму ещё одно поле, для фамилии (last name) пользователя. Очевидно, что для того, чтобы наладить правильную обработку данных, вводимых в это поле, нам понадобится добавить в состояние ещё одно свойство и поработать над механизмами, обновляющими состояние при вводе данных в поле.

Один из подходов к решению этой задачи заключается в создании отдельного обработчика событий для нового поля. Для маленькой формы с несколькими полями ввода это — вполне нормальный подход, но если речь идёт о форме с десятками полей, создавать для каждого из них отдельный обработчик события onChange — не лучшая идея.

Для того чтобы в одном и том же обработчике событий различать поля, при изменении которых он вызывается, мы назначим полям свойства name, которые сделаем точно такими же, какими сделаны имена свойств, используемых для хранения данных полей в состоянии (firstName и lastName). После этого мы сможем, работая с объектом события, который передаётся обработчику, узнать имя поля, изменения в котором привели к его вызову, и воспользоваться этим именем. Мы будем пользоваться им, задавая имя свойства состояния, в которое хотим внести обновлённые данные. Вот код, который реализует эту возможность:

import React, {Component} from "react"

class App extends Component {
    constructor() {
        super()
        this.state = {
            firstName: "",
            lastName: ""
        }
        this.handleChange = this.handleChange.bind(this)
    }
    
    handleChange(event) {
        this.setState({
            [event.target.name]: event.target.value
        })
    }
    
    render() {
        return (
            <form>
                <input type="text" name="firstName" placeholder="First Name" onChange={this.handleChange} />
                <br />
                <input type="text" name="lastName" placeholder="Last Name" onChange={this.handleChange} />
                <h1>{this.state.firstName} {this.state.lastName}</h1>
            </form>
        )
    }
}

export default App

Обратите внимание на то, что, задавая имя свойства объекта, передаваемого setState(), мы заключаем конструкцию event.target.name в прямоугольные скобки.

Учебный курс по React, часть 23: первое занятие по работе с формами - 3

Страница приложения в браузере

На страницу теперь выводится то, что вводится в первое поле, и то, что вводится во второе поле.

Принципы работы с текстовыми полями, которые мы только что рассмотрели, справедливы и для других полей, основанных на них. Например, это могут быть поля для ввода адресов электронной почты, телефонов, чисел. Данные, вводимые в такие поля, можно обрабатывать, используя вышерассмотренные механизмы, для работы которых важно, чтобы имена полей соответствовали бы именам свойств в состоянии компонента, хранящих данные этих полей.

О работе с другими элементами форм мы поговорим на следующем занятии. Здесь же мы затронем ещё одну тему, которая имеет отношение к так называемым «управляемым компонентам» (controlled component), о которых вы, если посмотрели документацию [25] React по формам, уже кое-что читали.

Если нам нужно чтобы то, что выводится в поле, в точности соответствовало бы тому, что хранится в состоянии приложения, мы можем воспользоваться описанным выше подходом, при применении которого состояние обновляется по мере ввода данных в поле. Состояние является реактивным. А при использовании элементов формы, являющихся управляемыми компонентами, тем, что выводится в этих элементах, управляет состояние. Именно оно при таком подходе является единственным источником истинных данных компонента. Для того чтобы этого добиться, достаточно добавить в код описания элемента формы атрибут value, указывающий на соответствующее полю свойство состояния. Вот как будет теперь выглядеть код приложения.

import React, {Component} from "react"

class App extends Component {
    constructor() {
        super()
        this.state = {
            firstName: "",
            lastName: ""
        }
        this.handleChange = this.handleChange.bind(this)
    }
    
    handleChange(event) {
        this.setState({
            [event.target.name]: event.target.value
        })
    }
    
    render() {
        return (
            <form>
                <input 
                    type="text" 
                    value={this.state.firstName} 
                    name="firstName" 
                    placeholder="First Name" 
                    onChange={this.handleChange} 
                />
                <br />
                <input 
                    type="text" 
                    value={this.state.lastName} 
                    name="lastName" 
                    placeholder="Last Name" 
                    onChange={this.handleChange} 
                />
                <h1>{this.state.firstName} {this.state.lastName}</h1>
            </form>
        )
    }
}

export default App

После этих изменений приложение работает точно так же, как и раньше. Главное отличие от его предыдущей версии заключается в том, что в поле выводится то, что хранится в состоянии.

Хочу дать один совет, который избавит вас в будущем от ошибок, которые очень тяжело отлаживать. Вот как выглядит код обработчика событий onChange сейчас:

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

Рекомендуется, вместо прямого обращения к свойствам объекта event при конструировании объекта, передаваемого setState(), заранее извлечь из него то, что нужно:

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

Здесь мы не будем вдаваться в подробности, касающиеся ошибок, которых можно избежать, конструируя обработчики событий именно так. Если вам это интересно — почитайте о SyntheticEvent [26] в документации React.

Итоги

На этом занятии состоялось ваше первое знакомство с механизмами работы с формами в React. В следующий раз мы продолжим эту тему.

Уважаемые читатели! Пользуетесь ли вы какими-нибудь дополнительными библиотеками при работе с формами в React?

Автор: ru_vds

Источник [27]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/311924

Ссылки в тексте:

[1] Image: https://habr.com/ru/company/ruvds/blog/443214/

[2] Часть 1: обзор курса, причины популярности React, ReactDOM и JSX: https://habr.com/post/432636/

[3] Часть 2: функциональные компоненты: https://habr.com/post/433400/

[4] Часть 3: файлы компонентов, структура проектов: https://habr.com/post/433404/

[5] Часть 4: родительские и дочерние компоненты: https://habr.com/company/ruvds/blog/434118/

[6] Часть 5: начало работы над TODO-приложением, основы стилизации: https://habr.com/company/ruvds/blog/434120/

[7] Часть 6: о некоторых особенностях курса, JSX и JavaScript: https://habr.com/company/ruvds/blog/435466/

[8] Часть 7: встроенные стили: https://habr.com/company/ruvds/blog/435468/

[9] Часть 8: продолжение работы над TODO-приложением, знакомство со свойствами компонентов: https://habr.com/company/ruvds/blog/435470/

[10] Часть 9: свойства компонентов: https://habr.com/company/ruvds/blog/436032/

[11] Часть 10: практикум по работе со свойствами компонентов и стилизации: https://habr.com/company/ruvds/blog/436890/

[12] Часть 11: динамическое формирование разметки и метод массивов map: https://habr.com/company/ruvds/blog/436892/

[13] Часть 12: практикум, третий этап работы над TODO-приложением: https://habr.com/company/ruvds/blog/437988/

[14] Часть 13: компоненты, основанные на классах: https://habr.com/ru/company/ruvds/blog/437990/

[15] Часть 14: практикум по компонентам, основанным на классах, состояние компонентов: https://habr.com/ru/company/ruvds/blog/438986/

[16] Часть 15: практикумы по работе с состоянием компонентов: https://habr.com/ru/company/ruvds/blog/438988/

[17] Часть 16: четвёртый этап работы над TODO-приложением, обработка событий: https://habr.com/ru/company/ruvds/blog/439982/

[18] Часть 17: пятый этап работы над TODO-приложением, модификация состояния компонентов: https://habr.com/ru/company/ruvds/blog/439984/

[19] Часть 18: шестой этап работы над TODO-приложением: https://habr.com/ru/company/ruvds/blog/440662/

[20] Часть 19: методы жизненного цикла компонентов: https://habr.com/ru/company/ruvds/blog/441578/

[21] Часть 20: первое занятие по условному рендерингу: https://habr.com/ru/company/ruvds/blog/441580/

[22] Часть 21: второе занятие и практикум по условному рендерингу: https://habr.com/ru/company/ruvds/blog/443210/

[23] Часть 22: седьмой этап работы над TODO-приложением, загрузка данных из внешних источников: https://habr.com/ru/company/ruvds/blog/443212/

[24] Оригинал: https://scrimba.com/p/p7P5Hd/cW8Jdfy

[25] Вот: https://reactjs.org/docs/forms.html

[26] SyntheticEvent: https://reactjs.org/docs/events.html

[27] Источник: https://habr.com/ru/post/443214/?utm_source=habrahabr&utm_medium=rss&utm_campaign=443214