В данном уроке мы превратим наш 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 - просто "болванка". Нужно сделать их контролируемыми.
Подсказка:
Решение: (код сейчас не идеальный, но понятный. Рефакторить будем в конце раздела)
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-приложениях - не лучший вариант.
Разобьем задачу на этапы, необходимо:
Попробуйте сами, либо посмотрите решение:
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
атрибуте.
Мне нечего более здесь комментировать для тех, кто знает основы JavaScript. Для тех, кто не знает:
disabled = true
будет означать, что кнопка выключена. Кнопка должна быть выключена тогда, когда agree = false
(то есть, чекбокс не отмечен), значит мы делаем отрицание (НЕ) с помощью знака восклицания;Для добавления новости нам осталось сформировать код, который будет выводить в alert имя и текст новости. Я думаю, эта задача вам точно под силу.
Решение:
... onBtnClickHandler = (e) => { e.preventDefault() const { name, text } = this.state alert(name + '\n' + text) // \n = перенос строки }
Приехала обещанная валидация. Точнее, "Валидация часть 1: Начало".
Почему часть 1? Потому что, валидация форм и вообще работа с формой - одна из самых скрупулезных задач. Нужно показывать понятные сообщения об ошибках, подсвечивать неправильно заполненное поле, блокировать кнопку отправки, валидировать поля по определенным правилам и так далее.
В данный момент, мы добавим к условию (что чекбокс отмечен) только одно простое условие: поля name и text должны быть заполнены. И не пробелами.
Как бы решалась такая задача без react? Вероятно у нас была бы функция validate, которая вызывалась бы на каждое изменение в проверяемых полях. Нужно было бы генерировать и прослушивать событие...
Думаю, вы поняли намек. Здесь без state не обойтись, и это точно то место, где удобнее использовать именно состояние, а не refs.
Попробуйте сами, а потом сверьтесь с решением.
Задача: если в поле "имя" или "текст" не введено ничего (либо пробелы) - кнопка "показать alert" должна быть недоступной.
Подсказка #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 станет вам другом гораздо быстрее.
на данный момент.