Книга: react-course-ru-v2
Назад: Жизненный цикл компонента
Дальше: Добавить новость

Работа с формой

Работа с формой

В данном уроке мы превратим наш input в форму добавления новости. Научимся работать с чекбоксами, disabled атрибутом кнопки и прочими стандартными для такой задачи вещами.

Результатом добавления новости, пока что, вновь, будет alert с текстом новости.


Переименуйте <TestInput /> в <Add />, и рендерите в нем следующую форму: автор (input), текст новости (textarea), "я согласен с правилами" (checkbox), "показать alert" (button).

Попутно изменим названия классов, удалим автофокус, удалим лишние обработчики и переместим компонент <Add /> перед заголовком "Новости".

Итого, заготовка для "добавления новости" будет выглядеть следующим образом:

class Add extends React.Component {   onBtnClickHandler = (e) => {     e.preventDefault()   }   render() {     return (       <form className='add'>         <input           type='text'           className='add__author'           placeholder='Ваше имя'         />         <textarea           className='add__text'           placeholder='Текст новости'         ></textarea>         <label className='add__checkrule'>           <input type='checkbox' /> Я согласен с правилами         </label>         <button           className='add__btn'           onClick={this.onBtnClickHandler}>           Показать alert         </button>       </form>     )   } } 

Если вы не против моего оформления, можете взять стили для компонента <Add />:

.add {   margin: 0 5px 5px 0;   width: 210px;   border: 1px dashed rgba(0, 89, 181, 0.82);   padding: 5px; } .add__author, .add__text, .add__btn, .add__checkrule {   display: block;   margin: 0 0 5px 0;   padding: 5px;   width: 94%;   border: 1px solid rgba(0, 89, 181, 0.82); } .add__checkrule {   border: none;   font-size: 12px; } .add__btn {   box-sizing: content-box;   color: #FFF;   text-transform: uppercase;   background: #007DDC; } .add__btn:disabled {   background: #CCC;   color: #999; } 

Так как мы близки к финалу, я бы хотел нагрузить вас работой.

Задача: сейчас инпут "ваше имя" и text area - просто "болванка". Нужно сделать их контролируемыми.

add-news-task-1

Подсказка:

  • создайте state (начальное состояние)
  • добавьте обработчики на изменение имени и текста новости
  • в value элементов записывайте значение переменной из состояния

Решение: (код сейчас не идеальный, но понятный. Рефакторить будем в конце раздела)

class Add extends React.Component {   state = { // добавили начальное состояние     name: '',     text: '',   }   onBtnClickHandler = (e) => {     e.preventDefault()   }   handleNameChange = (e) => { обработчик, в котором обновляем name     this.setState({ name: e.currentTarget.value })   }   handleTextChange = (e) => { обработчик, в котором обновляем text     this.setState({ text: e.currentTarget.value })   }   render() {     const { name, text } = this.state // вытащили значения из стейта      // добавили value для name и для textarea     return (       <form className='add'>         <input           type='text'           onChange={this.handleNameChange}           className='add__author'           placeholder='Ваше имя'           value={name}         />         <textarea           onChange={this.handleTextChange}           className='add__text'           placeholder='Текст новости'           value={text}         ></textarea>         <label className='add__checkrule'>           <input type='checkbox' /> Я согласен с правилами         </label>         <button           className='add__btn'           onClick={this.onBtnClickHandler}>           Показать alert         </button>       </form>     )   } } 

Инпуты начали работать и у нас есть очень приятный бонус: имя и текст новости хранится в this.state. Удобно? Разумеется. Представьте, что мы будем делать валидацию формы. У нас в любой момент времени будут актуальные значения имени и текста новости! Все еще не в восторге? Немного терпения и мы доберемся до валидации...

Однако, для начала заставим работать связку чекбокс + кнопка отправки новости.


Давайте отключим кнопку "показать alert", если не отмечен чекбокс. Здесь есть 2 варианта - использовать state или не использовать. Для нашей задачи никаких проблем с производительностью не будет, если мы будем использовать state. Лазить в DOM (даже с помощью refs) в React-приложениях - не лучший вариант.

Разобьем задачу на этапы, необходимо:

  • добавить значение в state для чекбокса (true/false);
  • добавить атрибут disabled у кнопки равным значению из state;
  • добавить функцию обработчик;

Попробуйте сами, либо посмотрите решение:

class Add extends React.Component {   state = {     name: '',     text: '',     agree: false, // новое значение состояния - agree (булево)   }   onBtnClickHandler = (e) => {     e.preventDefault()   }   handleNameChange = (e) => {     this.setState({ name: e.currentTarget.value })   }   handleTextChange = (e) => {     this.setState({ text: e.currentTarget.value })   }   handleCheckboxChange = (e) => { // обработчик кликов по чекбоксу     // чтобы установить true/false считываем свойство checked     this.setState({ agree: e.currentTarget.checked })   }   render() {     const { name, text, agree } = this.state     return (       <form className='add'>         <input           type='text'           onChange={this.handleNameChange}           className='add__author'           placeholder='Ваше имя'           value={name}         />         <textarea           onChange={this.handleTextChange}           className='add__text'           placeholder='Текст новости'           value={text}         ></textarea>         <label className='add__checkrule'>           <input type='checkbox' onChange={this.handleCheckboxChange} /> Я согласен с правилами         </label>         {/* кнопке добавили disabled равный (НЕ agree) */}         <button           className='add__btn'           onClick={this.onBtnClickHandler}           disabled={!agree}>           Показать alert         </button>       </form>     )   } } 

Так как по клику в чекбокс происходит изменение state, то вызывается перерисовка (метод render отработает). Значит, у нас всегда будет актуальное значение agree в disabled атрибуте.

checkbox plus button

Мне нечего более здесь комментировать для тех, кто знает основы JavaScript. Для тех, кто не знает:

  • (Кантор);
  • (Кантор)
  • disabled = true будет означать, что кнопка выключена. Кнопка должна быть выключена тогда, когда agree = false (то есть, чекбокс не отмечен), значит мы делаем отрицание (НЕ) с помощью знака восклицания;

Для добавления новости нам осталось сформировать код, который будет выводить в alert имя и текст новости. Я думаю, эта задача вам точно под силу.

alert new news

Решение:

... onBtnClickHandler = (e) => {   e.preventDefault()   const { name, text } = this.state   alert(name + '\n' + text) // \n = перенос строки } 

Блокировка кнопки, если не все поля заполнены

Приехала обещанная валидация. Точнее, "Валидация часть 1: Начало".

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

В данный момент, мы добавим к условию (что чекбокс отмечен) только одно простое условие: поля name и text должны быть заполнены. И не пробелами.

Как бы решалась такая задача без react? Вероятно у нас была бы функция validate, которая вызывалась бы на каждое изменение в проверяемых полях. Нужно было бы генерировать и прослушивать событие...

Думаю, вы поняли намек. Здесь без state не обойтись, и это точно то место, где удобнее использовать именно состояние, а не refs.

Попробуйте сами, а потом сверьтесь с решением.


Задача: если в поле "имя" или "текст" не введено ничего (либо пробелы) - кнопка "показать alert" должна быть недоступной.

add news validate

Подсказка #1: для удаления пробелов используйте стандартный метод

Подсказка #2: вам потребуется в атрибут disabled передавать результат работы функции validate (которую нужно создать в качестве метода компонента, чтобы был доступ к this.state). Пример:

validate = () => {   // какие то условия   // возвращает true или false } ... <button disabled={this.validate()} ... > ... 

Решение:

class Add extends React.Component {   state = {     name: '',     text: '',     agree: false,   }   ...   validate = () => {     const { name, text, agree } = this.state     if (name.trim() && text.trim() && agree) {       return true     }     return false   }   render() {     const { name, text, agree } = this.state     return (       <form className='add'>         ...         <button           className='add__btn'           onClick={this.onBtnClickHandler}           disabled={!this.validate()}>           Показать alert               </button>       </form>     )   } } 

Ну как? Не очень весело? Если да - значит у вас проблемы с основами JavaScript и вам нужно их подтягивать. А если в целом порядочек - я вас поздравляю, мы почти у цели.


Порефакторим копипасту

Есть проблемка:

handleNameChange = (e) => {   this.setState({ name: e.currentTarget.value }) } handleTextChange = (e) => {   this.setState({ text: e.currentTarget.value }) } 

Очень похожие методы. Можно ли их унифицировать? Конечно.

  • необходимо добавить вычисляемое значение ключа;
  • понять откуда будем считывать ключ;

Ключ будем считывать из id элемента. Идея такова: в id записываем такую же строку, что и значение ключа в state, то есть для name - инпуту дадим id='name', а для textarea - id='text'

Готовы?

class Add extends React.Component {   ...   handleChange = (e) => {     const { id, value } = e.currentTarget     this.setState({ [id]: e.currentTarget.value })   }   ...   render() {     const { name, text, agree } = this.state     return (       <form className='add'>         <input           id='name'           type='text'           onChange={this.handleChange}           className='add__author'           placeholder='Ваше имя'           value={name}         />         <textarea           id='text'           onChange={this.handleChange}           className='add__text'           placeholder='Текст новости'           value={text}         ></textarea>         ...       </form>     )   } } 

Из e.currentTarget мы можем считать id и value. Далее записываем в стейт по нужному ключу - значение.

Вычисляемое значение ключа - это одна из самых больных тем новичков. Внимательно читаем (и перечитываем) урок из учебника Кантора:


Итого: мы научились работать с формой. Положили начало досерверной валидации. Поняли и осознали, что знание основ JavaScript - это невероятный "буст" в изучении React. Да, без основ можно читать туториалы и выполнять задачи, но поверьте, если вы сначала подтянете основы, то React станет вам другом гораздо быстрее.

на данный момент.

Назад: Жизненный цикл компонента
Дальше: Добавить новость