четверг, 16 марта 2017 г.

Быстро с React: Как работать в React с формами.



Эта публикация описывает способы работы с текстом и прочими элементами ввода данных, такие как textarea или options. Умение работать с этими элементами является первостепенными навыками в веб программировании, потому что они позволяют вашему приложению получать данные ( например текст) и действия (например клик) от пользователей.
Исходный код к примерам из статьи находится в директории ch07 репозитория GitHub azat-co/react-quickly. Некоторые примеры можно найти по адресу http://reactquickly.co/demos.

Рекомендуемый способ работы с формами в React


В обычном HTML, когда мы работаем с элементами input, DOM страницы хранит значения этих элементов в узле DOM. И можно добраться до этого значения элемента с помощью метода document.getElementById('email').value или используя соответсвующие методы JQuery. DOM – это наше хранилище.
В React когда работаем с формами или другими элементами ввода, такие как отдельные текстовые поля или кнопки, у разработчиков появляется интересная пробелма которую необходимо решить. Из документации React: «Компоненты React должны представлять состояние отображения в любой момент времени, а не только в момент инициализации.» React позволяет использовать декларативнй стиль для описания UI. React описывает UI, его конечное состояние, и как он должен выглядеть.
Вы можете увидеть проблему? В традиционном HTML элементы формы, состояние элементов будут изменяться в процессе ввода данных пользователем. React использует декларативный подход к описанию UI. Элементы вводы должны быть динамичными для того что бы правильно отображать состояние.
Если разработчик планирует НЕ поддерживать состояние компонентов (в JavaScript) и не синхронизировать их с отображением, то появляется проблема: это может привести к ситуации, когда внутренне состояние и отображение отличаются. React не будет знать об этих изменениях.И это может привести к проблемам различного рода и смягчить простую философию React. Самый лучший способ заключается в том, что бы держать метод render() React’а как можно ближе к реальному DOM, и это включает и значения элементов ввода.
Давайте рассмотрим пример с текстовым полем. React должен включить новое значение в свой render() для данного компонента. Поэтому мы должны установить значение для элемента испольуя свойство value. Если мы реализуем поле <input> так как мы всегда делаем в HTML, React будет использовать render() для синхронизации с реальным DOM. React не позволит изменить это значение. Попробуйте сами. Это своидт меня с ума, но это нормальное поведение React!
render() {
  return <input type="text" name="title" value="Mr." />
}

Данный код представляет отображение в любом состоянии, и значение всегда будет «Mr.». Для элементов ввода мы должны изменить вывод в соответствие с требованиями пользователя. Таким образом, давайте сделаем значение динамическим.
Это реализация получше, потому что она обновляется из состояния:
render() {
  return <input type="text" name="title" value={this.state.title} />
}

А что такие значние состояния? React ничего не знает о пользовательском вводе в элементах формы. Разработчики должны реализовать обработчик сообщений для получения изменений в onChange.
handleChange(event) {
  this.setState({title: event.target.value})
}
render() {
  return &ltinput type="text" name="title" value={this.state.title} onChange={this.handleChange.bind(this)}/>
}

Имея это, лучшим способом для разработчика будет реализация следующих вещей для синхронизации с внутренним состоянием отображения (Изображение 1):
1.      Объявляем элементы в render()используя значения   из состояния.
2.      Обработка изменений элементов формы используя onChange()  по мере возникновения
3.      Обновление внутреннего состояния в обработчике
4.      Новые значения сохранены в состоянии и после этого отображение обновляется с помощью нового render()

 

Изображение 1: Обработка изменений и обновление состояний


На первый взгляд может показаться что это потребует много работы, но я надеюсь, что используя React больше, вы примете данный подход. Я его называю односторонняя привязка, потому что только состояние изменяет отображение. Здесь нет пути назад, только одностороннее направление от состояния к отображению. С односторонней привязкой, библиотека не обновит состояние или модель автоматически. Одним из преимуществ данного подхода является то что в этом случае нету сложности при работе с большимим количеством отображений, когда можно обновить множество состояний отдельно (модель данных)  и наоборот. Изображение 2.
Проще – не всегда значит меньше кода.Иногда, как в даном случае, разработчик должен писать дополнительный код, который вручную устанавливает данные из обработчиков событий (которые рендерятся в отображении), но этот подход становится приоритетным, когда приходится работать со сложным пользовательским интерфейсом и одностраничными приложениями с несчетным множеством отображений и состояний. Короче говоря: просто – не всегда легко.
 


Изображение 2: Односторонняя и двухсторонняя привязка


Наоборот, двухсторонняя привязка позволяет представлениям изменять состояние автоматически без явной реализации такой логики разработчиком. Двухсторонняя привязка реализована в Angular 1. Интересно, что Angular 2 позаимствовал принцип односторонней привязки у React и сделал ее привязкой по умолчанию (вы по прежнему можете явно указать необходимость использования двухсторонней привязки).
По этой причине, мы рассмотрим рекомендуемый подход работы с формами. Он называется контролируемые компоненты и гарантирует синхронизацию состяние внутреннего компонента с представлением.Альтернативный подход – неконтроллируемый компонент.
Таким образом мы изучили самый лучший вариант работы с полями ввода в React, который заключается в том, что бы перехватить изменения и применить их к состоянию как показанио на изображении 1 (ввод в измененное представление). Теперь давайте посмотрим как мы объявляем форму и ее элементы.

Определение формы и ее собыйтий в React

Мы начнем с элемента &lt;form&gt;. Разумеется, мы не хотим что бы элементы ввода размещались в DOM случайным образом. Если у нас будет несколько функциональных набороввходных данных, это может плохор закончится. Вместо этого мы обернем элементы ввода которые используются вместе с помощью элемента &lt;form&gt; &lt;/form&gt;.
Оборачивать в &lt;form&gt; необязательно. Можно использовать элементы по отдельности в простом случае. В более сложных сценариях разработчики могут группировать подобным образом элементы на одной странице. В таких случаях разумно использовать &lt;form&gt; для группировки. React из своей  &lt;form&gt; генерирует &lt;form&gt; HTML, и точно также со всеми всеми остальными элементами. В соответствии со спецификацией HTML разработчики не должны использовать формы вложенные одна в одну.
Элементы формы сами по себе могут иметь события. React поддерживает три события формы в дополнению к стандатрным событиям React:
·         onChange: срабатывает когда происходят изменения в каком либо элементе формы.
·         onInput:  срабатывает на каждом изменении значения textarea или input. Команда React не рекомендует использовать данное событие (смотрите ниже)
·         onSubmit: срабатывает при сабмите формы, как правило при нажатии на Enter

onChange vs. onInput

Событие  React onChange срабатывает на каждое изменение, в отличие от такого же события DOM, которое может срабатывать не на каждое изменение, а только при потере фокуса. Например для &lt;input type="text"&gt;  пользователь может печатать без onChange и только после того как перейдет к другому контролу, onChange будет инициирован со стороны HTML (стандартное событие браузера).  Как сказано ранее, в React, onChange срабатывает на каждое нажатие клавиши, а не только на потерю фокуса.
С другой стороны, onInput в React – это обертка над аналогом из DOM, который срабатывает на каждое изменение. Команда React рекомендует использовать onChange вместо onInput.
В конце концов onChange React работает иначе чем onChange в HTML, это выражается большим постоянством (похоже на onInput из HTML). onChange срабатывает на каждое изменение а не только при потере фокуса.
Рекомендуемый подход в React состоит в использовании onChange, а onInput только если вы хотите получить доступ к нативному поведению элемента управления. Причиной является то, что обертка onChange от React предоставляет гибкость.
В дополнение к трем событиям которые были описаны выше, &lt;form&gt; может содержать стандартные события React, такие как onKeyUp или onClick.   Использование событий формы может быть удобно если вам к примеру требуется получить какое то конкретное событые для всей формы. (группы элементов ввода).
Например, хорошим решением будет разрешить пользователям отсылать данные на сервер по нажатию на Enter (если предположить что вы не используете textarea, где такое поведение неприменимо, так как нажатие на ввод должно приводить к переходу на следующую строку). Вы можете прослушивать событие сабмита с помощью создания прослушивателя события который запускается с помощью this.handleSubmit().
handleSubmit(event) {
  ...
}
render() {
  <form onSubmit={this.handleSubmit}>
    <input type="text" name="email" />
  </form>
}

Обратите внимание: Мы должны реализовать функцию handleSubmit за пределами render(), как бы мы сделали с любым другим событием. В React нету никаких требований к праилам именования, поэтому вы можете называть ваши обработчики событий как вам нравится, оставляя названия тем не менее понятными и логичными. Сейчас, мы обратимся к наиболее популярной нотации и добавим префикс к обработчику событий – добавим слово “handle”, которое будет отличать его от прочихобычных методов класса.
Напомню, не вызывайте метод (не добавляйте скобки) и не используйте двойные кавычки вокруг фигурных скобок (правильный вариант: EVENT={this.METHOD}) когда задаете метод обработчика. Я знаю что для некоторых читателей это все основы JavaScript и очевидно, но вы не поверите, сколько раз я видел ошибки связанные с этими моментами в коде React: мы передаем объявление функции, не ее результат и мы используем фигурные скобки как значение для JSX аттрибута.
Другой способ реализации подписки на Enter – это вручную прослушивать событие нажатия на клавишу (onKeyUp) и проверка кода нажатой клавиши (13 для Enter).
handleKeyUp(event) {
  if (event.keyCode == 13) return this.sendData()
}
render() {
  return <form onKeyUp={this.handleKeyUp}>
  ...
  </form>
}

Обратите внимание что метод sendData реализован где то еще в коде. Также мы должны вызвать bind(this) для обработчика в constructor().
Сумируя все сказанное, мы можем иметь события для элементов формы, а не только для отдельных элементов в форме.

Источник: https://www.sitepoint.com/work-with-forms-in-react/