Глава 4 из книги; Vumbula React - Соавтор Эдмонд Атто
Резюме:
- В Главе 1 познакомьтесь с React с ES6. Если вы новичок в React, просто нуждаетесь в освежении знаний или нуждаетесь в легком ознакомлении с функциями ES6, которые наиболее часто используются в этой книге, просмотрите первую главу.
- В Главе 2 , познакомьтесь с компонентами React. Они являются строительными блоками любого приложения React, которое вы создадите. Если вы еще не знакомы с компонентами React, просмотрите эту главу, а затем вернитесь сюда.
- В Главе 3 познакомьтесь с State in React. Понимание состояния и того, как оно работает, откроет вам возможность создавать мощные компоненты. Если вам еще не нравится React State, просмотрите эту главу, а затем возвращайтесь сюда.
Большинство приложений, которые вы создадите, будут иметь большую часть своей функциональности, сосредоточенной на обнаружении и реагировании на ввод пользователя. Это может быть щелчок или, что более вероятно, форма. В этой главе мы сосредоточимся на формах и убедимся, что вы хорошо понимаете, как формы используются в React.
Формы - это наиболее распространенный способ получения ввода от пользователя, например, формы используются для сбора данных для входа в систему. Когда пользователь нажимает кнопку входа в систему, эти данные отправляются, и затем они могут быть переданы серверной службе (аутентификации) приложения для обработки. В зависимости от того, был ли вход в систему успешным, интерфейс обновляется соответствующим образом. Следовательно, формы также позволяют пользователям обновлять уже существующую информацию, такую как их имя пользователя, на сайте социальной сети, когда они считают, что нашли более интересную информацию.
При работе с формами в React обычно используются два типа компонентов:
= ›Управляемые компоненты
= ›Неконтролируемые компоненты
Контролируемые компоненты
Элементы формы HTML уникальны, потому что по умолчанию они поддерживают некоторое внутреннее состояние. В частности, элементы формы, такие как <input>
и <textarea>
, поддерживают и обновляют свое собственное внутреннее состояние. По этой причине мы должны более тщательно подумать о том, как мы используем их в React.
В главе 3 указывается, что изменяемые данные компонента хранятся в его свойстве состояния. Тогда имеет смысл объединить естественные возможности HTML-форм с состоянием React, чтобы сделать состояние React единственным источником данных.
Эта комбинация создает ситуацию, когда компонент, отображающий форму, также контролирует, какие действия выполняются при вводе пользователем. По этой причине такой компонент называется управляемым компонентом. Входы, которые находятся внутри управляемых компонентов, известны как управляемые входы.
Вот пример управляемого компонента, который отображает форму входа. В демонстрационных целях мы будем использовать поле username
.
В приведенном выше примере компонент LoginForm
настроен с объектом состояния, содержащим свойство username
. Это свойство будет содержать значение / текст, введенный пользователем.
Изначально текст в поле ввода не отображается, потому что для его атрибута value установлено значение this.state.username
, которое инициализируется с помощью имени пользователя, установленного на пустую строку.
Когда пользователь щелкает поле ввода и начинает печатать, каждое нажатие клавиши запускает обработчик событий onChange
. Затем вызывается функция handleChange
, и текущее значение (текст) во входных данных сохраняется в состоянии с использованием setState()
.
setState()
вызывает повторную визуализацию компонента, и текст, отображаемый в поле ввода, теперь выбирается из this.state.username
. Текст в h3
также обновляется при повторном рендеринге.
Этот поток гарантирует, что input
, h3
и state
всегда синхронизированы, поскольку объект состояния является единственным источником истины для компонента.
Использование контролируемых компонентов гарантирует, что:
= ›входные данные (поле имени пользователя в этом примере) и данные (состояние) всегда синхронизируются
=› пользовательский интерфейс (тегh3
в в этом примере) и данные (состояние) всегда синхронизированы.
Работа с несколькими входами
Маловероятно, что в приложении будет форма всего с одним полем. Давайте добавим дополнительное поле к компоненту LoginForm
из предыдущего, чтобы изучить, как реализовать несколько контролируемых входов с минимальной разметкой.
Чтобы использовать одну функцию handleChange
для нескольких входов, каждому полю ввода присваивается атрибут name
. Функция handleChange
изменяется для выполнения различных действий в зависимости от входа target
. Здесь мы используем всю мощь имени вычисляемого свойства ES6 [name]: value
для обновления ключа состояния, соответствующего атрибуту name
конкретного входа.
Неконтролируемые компоненты
В неконтролируемых компонентах данные формы обрабатываются DOM, в отличие от контролируемых компонентов, в которых данные формы обрабатываются компонентом React.
Неконтролируемые компоненты используют тот факт, что элементы формы HTML поддерживают свое собственное внутреннее состояние. При работе с неконтролируемыми входами управление состоянием через компонент React не требуется.
В неконтролируемых компонентах доступ к данным формы осуществляется с помощью ссылок. Думайте о ref как о бирке, которую вы получаете, когда регистрируете свою сумку в аэропорту (просто возьмите ее с собой). Когда ваш рейс приземляется, вы предъявляете свой тег, который служит ссылкой на то, какая сумка принадлежит вам. Сотрудник стойки регистрации берет вашу бирку и через несколько минут возвращается с вашей сумкой. Точно так же HTML-формы знают, какие данные принадлежат какому полю ввода, и, назначив входу ссылку, вы можете позже получить его значение.
По этой аналогии вы можете получить свою сумку только после приземления самолета. Точно так же вы можете использовать ссылки для получения данных формы только после ее отправки.
Вот пример неконтролируемого компонента.
В этом примере обратите внимание, что вход имеет атрибут ref
. Элемент ввода передается как ввод в функцию стрелки, а затем присваивается this.input
.
Когда форма отправляется, запускается функция handleSubmit
, и в этот момент текст, введенный пользователем, может быть доступен с помощью this.input.value
.
Использование значений по умолчанию в контролируемых компонентах
В случаях, когда пользователю необходимо обновить уже существующее значение, например, обновление профиля, поле ввода должно отображать уже существующее значение и оставаться доступным для редактирования.
Во время жизненного цикла рендеринга компонента React атрибут value
формы всегда будет иметь приоритет над атрибутом value
в DOM. Следовательно, вы можете использовать React, чтобы установить начальное значение и оставить последующие обновления как неконтролируемые.
В приведенном выше компоненте LoginForm
поле ввода изначально отображается с текстом cool-guy из-за значения, переданного в атрибут defaultValue
. В качестве альтернативы сюда можно передать значение из состояния.
После отправки формы атрибут значения поля ввода переопределяет значение по умолчанию.
Контролируемое против неконтролируемого
Использование контролируемых компонентов широко рассматривается как предпочтительный способ работы с формами в React. Это потому, что они более мощные, чем неконтролируемые компоненты, и предлагают ряд преимуществ, а именно:
= ›Входные данные, данные и пользовательский интерфейс всегда синхронизированы
= ›Они позволяют мгновенную проверку полей
= ›Они позволяют настраивать форматирование ввода перед отправкой, например, преобразование всех введенных адресов электронной почты в нижний регистр перед отправкой формы.
Тем не менее, использование контролируемых компонентов, в которых задействованы формы с многочисленными полями ввода, может быть утомительным. Это связано с тем, что вам потребуется написать onChange
обработчика, охватывающего все возможные способы изменения ваших данных, и направлять все входные данные через компонент React.
В ситуациях, когда форма, с которой вы имеете дело, относительно проста и требует только отправки пользовательских данных без динамических обновлений пользовательского интерфейса во время ввода пользователем или форматирования ввода перед отправкой, неконтролируемые компоненты могут быть лучшим выбором.
Проект Третий (Продолжение)
Теперь вы знаете достаточно о формах, чтобы создать остальные функции третьего проекта из предыдущей главы.
Добавление данных формы в состояние
Пришло время использовать форму вверху страницы, чтобы добавить names
и соответствующие им ages
. Для этого необходимо внести некоторые изменения в компонент класса, особенно в элемент формы.
Чтобы получить name
и age
, введенные пользователем, нам нужно добавить атрибут ref
к элементам ввода name
и age
. Атрибут ref
принимает обратный вызов, который получает базовый элемент DOM в качестве аргумента.
Затем обратный вызов ref
используется для хранения ссылки на текстовый ввод элемента DOM в переменной экземпляра, в нашем случае переменными экземпляра являются name
и age
, как показано в фрагменте кода ниже.
Добавьте строки ref
к элементам ввода вашего имени и возраста, чтобы они соответствовали приведенному выше коду.
Теперь, когда у нас есть ссылка на текст, введенный пользователем, нам нужно добавить его в состояние при нажатии кнопки сохранения. Чтобы сделать это эффективно, нам нужно добавить обработчик событий onSubmit
к элементу формы, который будет вызываться при нажатии кнопки сохранить (отправить). Атрибут обработчика onSubmit
ожидает, что функция будет выполнена при нажатии кнопки сохранить.
Поэтому определите стрелочную функцию с именем onSubmit
, которая принимает событие как единственный аргумент в компоненте Application
. В функции onSubmit
предотвратите поведение кнопки по умолчанию (перезагрузку страницы при нажатии), добавив event.preventDefault()
.
Нам также нужно получить текст name
и age
, введенный пользователем, из переменных экземпляра, которые мы установили в обратных вызовах ref
. После всего этого мы обновляем состояние компонента, используя this.setState
, как показано в фрагменте кода ниже.
Наконец, добавьте атрибут onSubmit
к элементу формы и this.onSubmit
в качестве его значения, ссылающегося на функцию onSubmit, определенную в компоненте.
<form className="form-inline" onSubmit={this.onSubmit}>
Теперь откройте файл index.js
в браузере и введите kagga в поле ввода имени и 30 в поле ввода возраста, затем нажмите кнопку сохранить. На страницу будет добавлена новая карточка, как показано на изображении ниже.
Неизменность состояния
Теперь вы можете добавить данные формы в состояние и отобразить их на странице.
Но подождите ... вы это видели? Ничего страшного, если вы этого не сделали. Во введении к главе 3 было указано, что состояние в React никогда не должно изменяться, то есть должно быть неизменным.
Оглядываясь назад на функцию onSubmit
, мы изменили состояние, когда использовали метод push
для массива данных из this.state.data
.
Правильный способ обновить состояние - создать новый массив данных, а затем обновить состояние этим новым массивом. Этого можно добиться разными способами, но мы собираемся использовать оператор распространения ES6 для создания нового массива данных, а также добавить новый информационный объект, содержащий name
и age
из формы, как показано в фрагменте кода ниже. Внесите необходимые изменения в предыдущий код.
С приведенными выше изменениями мы по-прежнему получаем те же результаты с дополнительным преимуществом неизменности состояния. Найдите весь код для этого раздела здесь.
Проект четвертый: создание приложения со списком покупок
В этом проекте мы объединим все, что мы узнали до этого момента, при создании приложения со списком покупок. Наше приложение позволит использовать CRUD-функции.
Стартовые файлы доступны для загрузки здесь, там вы найдете TODO, которые помогут вам, если вы хотите попробовать проект самостоятельно. Окончательный код проекта также доступен здесь и содержит решения для всех TODO в стартовом коде.
После клонирования репозитория cd
в каталог проекта и установите зависимости, запустив npm install
или yarn install
. Запустите npm start
или yarn start
, чтобы просмотреть проект в браузере и убедиться, что все работает нормально.
В папке src вы найдете файл App.js
, содержащий App
компонент. Внутри компонента App
есть функциональные компоненты, в том числе Nav
, Jumbotron
, AddItem
и Footer
.
Добавление товаров в список покупок
- Добавьте
name
иprice
как свойства к объекту состояния. Они будут содержать новый элемент до того, как он будет сохранен в состоянии. - После того, как
name
иprice
были сохранены как новый элемент в массиве элементов, который находится в состоянии, они сбрасываются до значений по умолчанию.
Уничтожьте name
и price
из объекта состояния и передайте их в качестве свойств компоненту AddItem
.
- Также в компоненте
AddItem
деструктурируйтеname
иprice
в скобках аргумента функции. - Добавьте атрибут значения к входным элементам
name
иprice
с удаленными переменными из списка аргументов компонента по мере необходимости. - Добавьте имя атрибута к входным элементам
name
иprice
со строковыми значениями имени и цены соответственно. - Нам нужно проверить тип свойств, которые мы передаем компоненту
AddItem
. Для этого мы используем пакетprop-types
. Следуйте инструкциям ниже, чтобы добавить проверку типа. - Импортируйте
PropTypes
из пакетаprop-types
, обратите внимание, что этот пакет уже установлен, он является частью зависимостей вpackage.json
, но не связан с React. Его всегда следует устанавливать отдельно, используяnpm
.
Добавьте объект proptypes
для name
и price
с типом строки и пометьте их, как показано ниже.
На этом этапе ваш компонент должен выглядеть так:
Чтобы получить name
и price
пользовательские типы в поля ввода, нам нужно добавить прослушиватель событий onChange
как на входы name
, так и price
.
- Создайте стрелочную функцию с именем
handleInputChange
, которая принимаетevent
в качестве собственного аргумента в компоненте приложения. - Внутри функции используйте переданный параметр
event
, чтобы получить элемент вводаtarget
; изtarget
получитьvalue
иname
элемента ввода.
Используйте функцию setState
, чтобы добавить name
и / или price
к свойствам name
и price
в объекте состояния.
Определите свойство onChange
для компонента AddItem
со значением this.handleInputChange
<AddItem name={name} price={price} onChange={this.handleInputChange} />
- В файле и компоненте
AddItem
добавьте опоруonChange
в список деструктурированных элементов в списке аргументов функции. - Добавьте
onChange
к объекту propTypes в качестве обязательной функции. - Добавьте атрибут
onChange
к обоим элементам ввода со значением свойстваonChange
.
На этом этапе ваш AddItem
компонент должен выглядеть так
- Зайдите в браузер, давайте посмотрим, как мы продвигаемся.
Откройте инструменты разработчика React и посмотрите раздел состояния. Обратите внимание, что в разделе состояний свойства name
и price
имеют в качестве значений пустые строки.
По мере ввода в поля ввода name
или price
состояние обновляется при каждом нажатии клавиши.
name
иprice
теперь нужно добавить в массив элементов в состоянии, чтобы они отображались при нажатии кнопкиsave
. Давай сделаем это сейчас.- Определите стрелочную функцию с именем
addItem
, которая принимаетevent
в качестве единственного аргумента - В нем вызывается
preventDefault()
при событии, чтобы предотвратить поведение кнопки по умолчанию. - Используйте деструкцию, чтобы получить набор
name
иprice
из состояния. - Поскольку
id
необходим при сохранении элемента, который будет использоваться в качестве ключа, получите длину существующего массива элементов в состоянии. Затем используйте тернарный оператор, чтобы либо увеличитьid
последнего элемента в массиве элементов, либо использовать1
в качествеid
, если массив элементов пуст.
Используйте функцию setState
, чтобы добавить новый элемент в массив элементов. Помните, что нельзя изменять состояние. Используйте оператор распространения для существующих элементов в массиве и функцию Object.assign
для добавления нового элемента в массив. Верните name
и price
к значениям по умолчанию, как показано ниже.
- Определите свойство
onSubmit
для компонентаAddItem
со значениемthis.addItem
в компоненте приложения. - Внутри компонента
AddItem
добавьтеonSubmit
в список деструктурированных элементов в списке аргументов функции. - Добавьте
onSubmit
к объекту proptypes в качестве обязательной функции. - Добавьте в форму атрибут
onSubmit
со значениемonSubmit
. - Момент истины, откройте приложение в браузере. На этом этапе вы сможете просмотреть добавленный элемент, нажав кнопку «Сохранить».
Окончательный рабочий код этого раздела можно найти здесь.
Редактирование / обновление товаров в списке покупок
В этом разделе мы займемся редактированием и обновлением элементов. Общая идея состоит в том, чтобы щелкнуть кнопку редактирования, чтобы поля name
и price
превратились в поля ввода, тем самым давая пользователю возможность изменять их содержимое. После изменения name
и price
пользователь может нажать кнопку save
, чтобы name
и price
вернулись в свой режим отображения. Стартовый код для этого раздела можно найти здесь.
Давайте начнем
- Определите стрелочную функцию с именем
toggleItemEditing
, которая принимаетindex
как единственный аргумент.index
будет использоваться для поиска редактируемого элемента. - В этой функции используйте метод
setState
и в нем определитеkey
элемента. Чтобы установить его значение, выполните цикл по массивуitems
, и когда будет найден элемент с переданным вindex
, добавьте свойствоisEditing
со значением!item.isEditing
. Это соответственно переключит логическое значениеisEditing
. Реализация функции показана ниже.
- Добавьте
toggleEdit
в качестве опоры к компонентуItemCard
и определите стрелочную функцию, которая вызывает функциюtoggleItemEditing
, передавая ейindex
в качестве аргумента. - Эта функция действует как обратный вызов и будет выполняться только при нажатии кнопки.
toggleEditing = {() => this.toggleItemEditing(index)}
- В компоненте
ItemCard
добавьте атрибутonClick
к кнопке редактирования со свойствомtoggleEditing
в качестве его значения. - Используйте свойство
isEditing
элемента для переключения между отображением Изменить или Сохранить в качестве текста кнопки. Не забудьте добавитьtoggleEditing
в список propTypes в компоненте ItemCard.
<button type="button" className="btn btn-primary mr-2" onClick={toggleEditing} > {item.isEditing ? "Save" : "Edit"} </button>
- На этом этапе при нажатии кнопки редактирования в браузере текст будет переключаться между Сохранить и Изменить.
- Также обратите внимание, что свойство
isEditing
изменяет свое значение всякий раз, когда нажимается кнопка Edit, как показано ниже в React devtools.
- Теперь мы используем свойство
item.isEditing
для визуализации полей ввода или отображенияname
иprice
элемента в теле карты.
Также добавьте атрибут value
к элементу ввода name
со значением item.name
, а также элемент ввода price
с item.price
.
На этом этапе, когда вы открываете приложение в браузере и нажимаете кнопку edit
, поля ввода должны быть видны. При нажатии кнопки save
они должны исчезнуть, как показано ниже.
Обратите внимание, что попытка ввести данные в поля item-name
и item-price
на этом этапе не сработает. Это потому, что мы используем контролируемые входы, но у входов нет onChange
обработчиков событий. Исправить это довольно быстро, нам нужно написать функцию для обработки функций редактирования.
- В компоненте App определите стрелочную функцию с именем
handleItemUpdate
, которая принимаетevent
иindex
в качестве своих единственных аргументов. - Эта функция похожа на ту, которую мы определили выше, которая обновляла состояние с помощью
name
иprice
элемента перед его сохранением в массив элементов. Разница в том, что мы используем функциюsetState
, чтобы найти элемент с переданным вindex
и обновить егоname
и / илиprice
новыми значениями. Мы используем оператор распространения, чтобы заполнить уже существующие свойства элемента.
Мы возвращаем товар после обновления, как показано ниже.
К настоящему времени вы знаете поток. Продолжайте и добавьте опору onChange
к компоненту ItemCard
с указанной выше функцией в качестве ее значения.
Добавьте переданную опору в список аргументов компонента ItemCard
и используйте стрелочную функцию, которая принимает event
. Эта функция возвращает эту опору как значение атрибута onChange
для входных элементов name
и price
, передавая ему event
и index
, как показано ниже.
onChange = {event => onChange(event, index)}
Имя опоры
onChange
может иметь любое имя. ЗдесьonChange
используется для простоты, но атрибутonChange
в элементах ввода НЕ МОЖЕТ иметь другое имя.
Компонент ItemCard
в итоге выглядит так.
Теперь приложение должно разрешить обновление name
и / или price
любого элемента. Окончательный код для этого раздела найдите здесь.
Удаление товара из списка покупок
Удаление элемента должно быть простым. Идея состоит в том, что когда пользователь нажимает кнопку удалить, элемент удаляется из массива элементов в состоянии. Вот - стартовый код для этого раздела.
Приступим к добавлению функции удаления.
- Определите стрелочную функцию
onDelete
, которая принимаетindex
в качестве единственного аргумента. - Внутри функции вызовите функцию
setState
и определите объект с элементами в качестве ключа свойства и значением, являющимся пустым массивом. - Внутри массива используйте оператор распространения, чтобы заполнить массив элементами от нулевого индекса до элемента перед переданным в индексе с помощью метода
slice
.
На данный момент только часть массива включается в новый массив с помощью оператора распространения. Чтобы добавить оставшуюся часть массива без элемента с переданным индексом (элемент, который нужно удалить), снова используются оператор распространения и метод среза для получения элементов в index
, переданных в + 1
, как показано ниже.
onDelete = index => { this.setState({ items: [ ...this.state.items.slice(0, index), ...this.state.items.slice(index + 1) ] }); };
- Двигаясь дальше, определите свойство
onDelete
в компонентеItemCard
, значение которого будет функцией стрелки, которая вызывает функциюonDelete
в компоненте приложения, передавая ейindex
элемента, который нужно удалить. - Внутри компонента
ItemCard
деструктурируйте опоруonDelete
в списке аргументов компонентов. - Продолжайте и добавьте
onDelete
к компонентамpropTypes
.
Наконец, добавьте атрибут onClick
к кнопке удаления со свойством onDelete
в качестве его значения, как показано ниже.
<button type="button" className="btn btn-primary" onClick={onDelete}> Delete </button>
Сохраните все свои изменения и откройте приложение в браузере, когда вы нажмете кнопку удаления, эта карточка товара должна быть удалена и, таким образом, исчезнет. Окончательный код для этого раздела найдите здесь.
Если вы нашли эту статью полезной, дотянитесь до кнопки 👏 и нажимайте ее столько раз, сколько вам понравилось читать этот пост. Мы также высоко ценим ваши отзывы. Вы также можете найти меня в твиттере.