Глава 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>

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

Если вы нашли эту статью полезной, дотянитесь до кнопки 👏 и нажимайте ее столько раз, сколько вам понравилось читать этот пост. Мы также высоко ценим ваши отзывы. Вы также можете найти меня в твиттере.