Работа с формой
В данном уроке мы превратим наш input в форму добавления новости. Научимся работать с чекбоксами, disabled атрибутом кнопки и прочими стандартными для такой задачи вещами.
Результатом добавления новости, пока что, вновь, будет alert с текстом новости.
Переименуйте <TestInput /> в <Add />, и рендерите в нем следующую форму: автор (input), текст новости (textarea), "я согласен с правилами" (checkbox), "показать alert" (button).
Попутно изменим названия классов, удалим автофокус, удалим лишние обработчики и переместим компонент <Add /> перед заголовком "Новости".
Итого, заготовка для "добавления новости" будет выглядеть следующим образом:
1
class Add extends React.Component {
2
onBtnClickHandler = (e) => {
3
e.preventDefault()
4
}
5
render() {
6
return (
7
<form className='add'>
8
<input
9
type='text'
10
className='add__author'
11
placeholder='Ваше имя'
12
/>
13
<textarea
14
className='add__text'
15
placeholder='Текст новости'
16
></textarea>
17
<label className='add__checkrule'>
18
<input type='checkbox' /> Я согласен с правилами
19
</label>
20
<button
21
className='add__btn'
22
onClick={this.onBtnClickHandler}>
23
Показать alert
24
</button>
25
</form>
26
)
27
}
28
}
Copied!
Если вы не против моего оформления, можете взять стили для компонента <Add />:
1
.add {
2
margin: 0 5px 5px 0;
3
width: 210px;
4
border: 1px dashed rgba(0, 89, 181, 0.82);
5
padding: 5px;
6
}
7
.add__author, .add__text, .add__btn, .add__checkrule {
8
display: block;
9
margin: 0 0 5px 0;
10
padding: 5px;
11
width: 94%;
12
border: 1px solid rgba(0, 89, 181, 0.82);
13
}
14
.add__checkrule {
15
border: none;
16
font-size: 12px;
17
}
18
.add__btn {
19
box-sizing: content-box;
20
color: #FFF;
21
text-transform: uppercase;
22
background: #007DDC;
23
}
24
.add__btn:disabled {
25
background: #CCC;
26
color: #999;
27
}
Copied!
Так как мы близки к финалу, я бы хотел нагрузить вас работой.
Задача: сейчас инпут "ваше имя" и text area - просто "болванка". Нужно сделать их контролируемыми.
add-news-task-1
Подсказка:
  • создайте state (начальное состояние)
  • добавьте обработчики на изменение имени и текста новости
  • в value элементов записывайте значение переменной из состояния
Решение: (код сейчас не идеальный, но понятный. Рефакторить будем в конце раздела)
1
class Add extends React.Component {
2
state = { // добавили начальное состояние
3
name: '',
4
text: '',
5
}
6
onBtnClickHandler = (e) => {
7
e.preventDefault()
8
}
9
handleNameChange = (e) => { обработчик, в котором обновляем name
10
this.setState({ name: e.currentTarget.value })
11
}
12
handleTextChange = (e) => { обработчик, в котором обновляем text
13
this.setState({ text: e.currentTarget.value })
14
}
15
render() {
16
const { name, text } = this.state // вытащили значения из стейта
17
18
// добавили value для name и для textarea
19
return (
20
<form className='add'>
21
<input
22
type='text'
23
onChange={this.handleNameChange}
24
className='add__author'
25
placeholder='Ваше имя'
26
value={name}
27
/>
28
<textarea
29
onChange={this.handleTextChange}
30
className='add__text'
31
placeholder='Текст новости'
32
value={text}
33
></textarea>
34
<label className='add__checkrule'>
35
<input type='checkbox' /> Я согласен с правилами
36
</label>
37
<button
38
className='add__btn'
39
onClick={this.onBtnClickHandler}>
40
Показать alert
41
</button>
42
</form>
43
)
44
}
45
}
Copied!
Инпуты начали работать и у нас есть очень приятный бонус: имя и текст новости хранится в this.state. Удобно? Разумеется. Представьте, что мы будем делать валидацию формы. У нас в любой момент времени будут актуальные значения имени и текста новости! Все еще не в восторге? Немного терпения и мы доберемся до валидации...
Однако, для начала заставим работать связку чекбокс + кнопка отправки новости.
Давайте отключим кнопку "показать alert", если не отмечен чекбокс. Здесь есть 2 варианта - использовать state или не использовать. Для нашей задачи никаких проблем с производительностью не будет, если мы будем использовать state. Лазить в DOM (даже с помощью refs) в React-приложениях - не лучший вариант.
Разобьем задачу на этапы, необходимо:
  • добавить значение в state для чекбокса (true/false);
  • добавить атрибут disabled у кнопки равным значению из state;
  • добавить функцию обработчик;
Попробуйте сами, либо посмотрите решение:
1
class Add extends React.Component {
2
state = {
3
name: '',
4
text: '',
5
agree: false, // новое значение состояния - agree (булево)
6
}
7
onBtnClickHandler = (e) => {
8
e.preventDefault()
9
}
10
handleNameChange = (e) => {
11
this.setState({ name: e.currentTarget.value })
12
}
13
handleTextChange = (e) => {
14
this.setState({ text: e.currentTarget.value })
15
}
16
handleCheckboxChange = (e) => { // обработчик кликов по чекбоксу
17
// чтобы установить true/false считываем свойство checked
18
this.setState({ agree: e.currentTarget.checked })
19
}
20
render() {
21
const { name, text, agree } = this.state
22
return (
23
<form className='add'>
24
<input
25
type='text'
26
onChange={this.handleNameChange}
27
className='add__author'
28
placeholder='Ваше имя'
29
value={name}
30
/>
31
<textarea
32
onChange={this.handleTextChange}
33
className='add__text'
34
placeholder='Текст новости'
35
value={text}
36
></textarea>
37
<label className='add__checkrule'>
38
<input type='checkbox' onChange={this.handleCheckboxChange} /> Я согласен с правилами
39
</label>
40
{/* кнопке добавили disabled равный (НЕ agree) */}
41
<button
42
className='add__btn'
43
onClick={this.onBtnClickHandler}
44
disabled={!agree}>
45
Показать alert
46
</button>
47
</form>
48
)
49
}
50
}
Copied!
Так как по клику в чекбокс происходит изменение state, то вызывается перерисовка (метод render отработает). Значит, у нас всегда будет актуальное значение agree в disabled атрибуте.
checkbox plus button
Мне нечего более здесь комментировать для тех, кто знает основы JavaScript. Для тех, кто не знает:
  • checked (Кантор);
  • disabled = true будет означать, что кнопка выключена. Кнопка должна быть выключена тогда, когда agree = false (то есть, чекбокс не отмечен), значит мы делаем отрицание (НЕ) с помощью знака восклицания;
Для добавления новости нам осталось сформировать код, который будет выводить в alert имя и текст новости. Я думаю, эта задача вам точно под силу.
alert new news
Решение:
1
...
2
onBtnClickHandler = (e) => {
3
e.preventDefault()
4
const { name, text } = this.state
5
alert(name + '\n' + text) // \n = перенос строки
6
}
Copied!

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

Приехала обещанная валидация. Точнее, "Валидация часть 1: Начало".
Почему часть 1? Потому что, валидация форм и вообще работа с формой - одна из самых скрупулезных задач. Нужно показывать понятные сообщения об ошибках, подсвечивать неправильно заполненное поле, блокировать кнопку отправки, валидировать поля по определенным правилам и так далее.
В данный момент, мы добавим к условию (что чекбокс отмечен) только одно простое условие: поля name и text должны быть заполнены. И не пробелами.
Как бы решалась такая задача без react? Вероятно у нас была бы функция validate, которая вызывалась бы на каждое изменение в проверяемых полях. Нужно было бы генерировать и прослушивать событие...
Думаю, вы поняли намек. Здесь без state не обойтись, и это точно то место, где удобнее использовать именно состояние, а не refs.
Попробуйте сами, а потом сверьтесь с решением.
Задача: если в поле "имя" или "текст" не введено ничего (либо пробелы) - кнопка "показать alert" должна быть недоступной.
add news validate
Подсказка #1: для удаления пробелов используйте стандартный метод trim()
Подсказка #2: вам потребуется в атрибут disabled передавать результат работы функции validate (которую нужно создать в качестве метода компонента, чтобы был доступ к this.state). Пример:
1
validate = () => {
2
// какие то условия
3
// возвращает true или false
4
}
5
...
6
<button disabled={this.validate()} ... >
7
...
Copied!
Решение:
1
class Add extends React.Component {
2
state = {
3
name: '',
4
text: '',
5
agree: false,
6
}
7
...
8
validate = () => {
9
const { name, text, agree } = this.state
10
if (name.trim() && text.trim() && agree) {
11
return true
12
}
13
return false
14
}
15
render() {
16
const { name, text, agree } = this.state
17
return (
18
<form className='add'>
19
...
20
<button
21
className='add__btn'
22
onClick={this.onBtnClickHandler}
23
disabled={!this.validate()}>
24
Показать alert
25
</button>
26
</form>
27
)
28
}
29
}
Copied!
Ну как? Не очень весело? Если да - значит у вас проблемы с основами JavaScript и вам нужно их подтягивать. А если в целом порядочек - я вас поздравляю, мы почти у цели.

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

Есть проблемка:
1
handleNameChange = (e) => {
2
this.setState({ name: e.currentTarget.value })
3
}
4
handleTextChange = (e) => {
5
this.setState({ text: e.currentTarget.value })
6
}
Copied!
Очень похожие методы. Можно ли их унифицировать? Конечно.
  • необходимо добавить вычисляемое значение ключа;
  • понять откуда будем считывать ключ;
Ключ будем считывать из id элемента. Идея такова: в id записываем такую же строку, что и значение ключа в state, то есть для name - инпуту дадим id='name', а для textarea - id='text'
Готовы?
1
class Add extends React.Component {
2
...
3
handleChange = (e) => {
4
const { id, value } = e.currentTarget
5
this.setState({ [id]: e.currentTarget.value })
6
}
7
...
8
render() {
9
const { name, text, agree } = this.state
10
return (
11
<form className='add'>
12
<input
13
id='name'
14
type='text'
15
onChange={this.handleChange}
16
className='add__author'
17
placeholder='Ваше имя'
18
value={name}
19
/>
20
<textarea
21
id='text'
22
onChange={this.handleChange}
23
className='add__text'
24
placeholder='Текст новости'
25
value={text}
26
></textarea>
27
...
28
</form>
29
)
30
}
31
}
Copied!
Из e.currentTarget мы можем считать id и value. Далее записываем в стейт по нужному ключу - значение.
Вычисляемое значение ключа - это одна из самых больных тем новичков. Внимательно читаем (и перечитываем) урок из учебника Кантора: Объекты как ассоциативные массивы
Итого: мы научились работать с формой. Положили начало досерверной валидации. Поняли и осознали, что знание основ JavaScript - это невероятный "буст" в изучении React. Да, без основ можно читать туториалы и выполнять задачи, но поверьте, если вы сначала подтянете основы, то React станет вам другом гораздо быстрее.
Исходный код на данный момент.
Last modified 1yr ago