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



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

image

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

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


> Оригинал

Формы являются достаточно важной частью веб-приложений. Но, как оказалось, у тех, кто занимается освоением 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

Обратите внимание на то, что для того, чтобы освоить материал этого занятия, вы должны быть знакомы с понятием состояния приложения. Если вы проработали все предыдущие занятия курса и самостоятельно выполняли практикумы, это значит, что вы обладаете знаниями, которые вам здесь понадобятся. Вот документация 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

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


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

Каждый символ, введённый в поле, тут же появляется в элементе <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 в прямоугольные скобки.


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

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

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

О работе с другими элементами форм мы поговорим на следующем занятии. Здесь же мы затронем ещё одну тему, которая имеет отношение к так называемым «управляемым компонентам» (controlled component), о которых вы, если посмотрели документацию 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 в документации React.

Итоги


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

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

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (0):