> For the complete documentation index, see [llms.txt](https://max-frontend.gitbook.io/react-course-ru-v2/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://max-frontend.gitbook.io/react-course-ru-v2/ispolzovanie-state/rabota-s-input.md).

# Работа с input

Сперва приберемся:

Удалим лишние console.log'и, удалим обработчик *handleCounter* и параграф, который выводил количество кликов.

Затем создадим компонент - `<TestInput />`, который будет просто отрисовывать (render) input перед списком новостей.

```jsx
...
// --- добавили test input ---
class TestInput extends React.Component {
  render() {
    return (
      <input className='test-input' value='введите значение' />
    )
  }
}

const App = () => {
  return (
    <React.Fragment>
      <h3>Новости</h3>
      <TestInput /> {/* добавили вывод компонента */}
      <News data={myNews}/>
    </React.Fragment>
  )
}
...
```

Напомню про комментарии:

Первый комментарий, добавлен с помощью `//`, так как данный комментарий не находится внутри JSX. А второй - находится, следовательно имеет вид `{/* комментарий */}`. То есть, JSX - это не весь код вашего сценария, а только те части, где мы миксуем верстку и js (обычно в return методе у render'a).

Вообще, код сейчас не работает (но это не из-за комментария). Давайте посмотрим на ошибку внимательно:

![readonly input error](/files/-LvLnSu6LzyK6aVF5nrI)

*Вы предоставили свойство value для поля, у которого нет onChange обработчика. Поэтому отрисовано поле только для чтения. Если поле должно быть изменяемо, используйте defaultValue. Либо установите onChange или readOnly. Проверьте render метод компонента TestInput.*

Не могу не любить react за такие подробные сообщения об ошибках.

А вы кстати попробуйте сейчас изменить значение инпута. Ничего не выйдет. Здесь у нас есть два пути, и первый нам известный - использовать какое-нибудь свойство *state* в качестве динамически изменяемого значения инпута.

## Controlled components (контролируемые компоненты)

Для вызова *setState*, будем использовать событие *onChange*. Работа с ним не отличается от работы с *onClick* или другими любыми событиями. Главное - передать функцию-обработчик.

Не торопитесь, давайте подумаем еще раз:

1. Нам нужно передать функцию обработчик, которая будет изменять какую-то переменную состояния (с помощью *setState*, разумеется).
2. Значит нам нужно создать начальное состояние (*state*).
3. Если у нас есть переменная состояния компонента, значит мы хотим, чтобы именно она была в качестве *value* у нашего инпута.

Сможете сделать сами? Если да - отлично, если нет - решение ниже.

Подсказка **#1**: так может выглядеть состояние + функция-обработчик

```javascript
...
state = { myValue: '' }
...
onChangeHandler = (e) => {
  this.setState({ myValue: e.target.value })
},
...
```

**Решение**:

```jsx
class TestInput extends React.Component {
  state = {
    myValue: '',
  }
  // используется e.currentTarget.value
  onChangeHandler = (e) => {
    this.setState({ myValue: e.currentTarget.value })
  }
  render() {
    return (
      <input
        className='test-input'
        onChange={this.onChangeHandler}
        value={this.state.myValue} 
        placeholder='введите значение' />
    )
  }
}
```

У нас есть *placeholder* - "введите значение", который будет показываться в момент загрузки страницы, так как наше начальное состояние input'a - пустая строка. При изменении, мы устанавливаем в переменную *myValue* - то что введено в input. Следовательно - input корректно изменяется.

![input edited](/files/-LvLnSuEc0oO7NP1yyFD)

## e.currentTarget vs e.target

Документация, что за свойство [currentTarget](https://developer.mozilla.org/ru/docs/Web/API/Event/currentTarget) (MDN)

Представьте ситуацию: у вас будет onClick стоять на `div`, внутри которого будет текст в параграфе. В итоге, при клике на текст в паграфе:

* e.target будет равно параграфу (и это верно, по спецификации)
* e.currentTarget будет равно div'у (и это то, что нам обычно и нужно)

Кто вообще не понял о чем идет речь - нужно читать [основы про объект события](https://learn.javascript.ru/obtaining-event-object) или весь раздел [основы работы с событиями](https://learn.javascript.ru/events-and-interfaces) целиком (Кантор).

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

**Задача:** По клику на кнопку - показывать alert с текстом инпута.

Попробуйте сами.

Подсказка **#1**:

Вам необходимо:

* добавить кнопку в `<TestInput />`;
* на кнопку "повесить" обработчик *onClick*;
* в функции обработчике считывать значение `this.state.myValue`;

Подсказка **#2**:

Так как нам необходимо рендерить больше одного элемента, нужно обернуть их в родительский элемент, например в `<div></div>` или `React.Fragment`

**Решение**:

```jsx
class TestInput extends React.Component {
  state = {
    myValue: '',
  }
  onChangeHandler = (e) => {
    this.setState({ myValue: e.currentTarget.value })
  }
  onBtnClickHandler = (e) => {
    alert(this.state.myValue);
  }
  render() {
    return (
      <React.Fragment>
        <input
          className='test-input'
          onChange={this.onChangeHandler}
          value={this.state.myValue}
          placeholder='введите значение' />
        <button onClick={this.onBtnClickHandler}>Показать alert</button>
      </React.Fragment>
    )
  }
}
```

Предлагаю добавить отступы для .test-input:

*css/app.css*

```css
...
.test-input {
  margin: 0 5px 5px 0;
}
...
```

После добавления отступа в данном коде ничего не раздражает. Или нет? Как думаете, что здесь может расстроить борца за оптимизацию?

**Ответ**: каждый раз, после любого изменения у нас вызывается *setState*, а значит - полная перерисовка компонента. Не очень приятно. Опять же, чуть больше логики в момент render'a компонента и в пору будет расстроиться от "отзывчивого" поля ввода.

У нас логики в render-методе никакой нет, поэтому для нас это лишняя оптимизация. Однако, рассмотреть способ создания "неконтролируемых компонентов" нужно обязательно.

Документация по react, предлагает вам использовать в большинстве случаев **контролируемые** компоненты.

## Uncontrolled Components (неконтролируемый компонент)

Главное отличие [неконтролируемого](https://reactjs.org/docs/uncontrolled-components.html) компонента от [контролируемого](https://reactjs.org/docs/forms.html#controlled-components) в том, что у него нет обработчика изменений, а значит нет постоянных вызовов *setState* и перерисовок.

Для того чтобы считать значение неконтролируемого компонента используется механизм refs.

Для неконтролируемого компонента в момент начальной загрузки можно указывать *defaultValue*.

Начнем по порядку:

1. Удалим обработчик *onChange*
2. Удалим *state*
3. Укажем *defaultValue* = пустая строка (`defaultValue=''`) вместо *value*
4. В constructor методе компонента (оп-па) укажем `this.input = React.createRef();`
5. Добавим атрибут *ref* инпуту, равный `this.input` (который в конструкторе создали)

```jsx
class TestInput extends React.Component {
  constructor(props) {
    super(props)
    this.input = React.createRef()
  }
  onBtnClickHandler = (e) => { // эта запись сейчас не работает
    alert(this.state.myValue);
  }
  render() {
    return (
      <React.Fragment>
          <input
            className='test-input'
            defaultValue=''
            placeholder='введите значение'
            ref={this.input}
          />
        <button onClick={this.onBtnClickHandler}>Показать alert</button>
      </React.Fragment>
    )
  }
}
```

Обновите страницу, попробуйте ввести значение. Работает? Работает!

Теперь нам нужно, научиться считывать значение: перепишите *onBtnClickHandler* следующим образом:

```javascript
onBtnClickHandler = () => {
  alert(this.input.current.value)
},
```

В [документации](https://reactjs.org/docs/uncontrolled-components.html#the-file-input-tag) так же есть пример использования refs и доступа к файлу, загружаемому через `<input type="file" />`.

За этот урок, мы научились с вами не вызывать дорогой *setState* и *render* на "каждый чих".

P.S. конечно, в данном случае никакого выигрыша в производительности нет. Оба подхода хорошо сработают.

Вариант с контролируемыми и неконтролируемыми компонентами, работа с *defaultValue* и *state* являются одинаковыми для всех элементов форм.

Очень рекомендую посмотреть страницу документации на англ.языке по [элементам форм](https://reactjs.org/docs/forms.html)

[Исходный код](https://github.com/maxfarseer/react-course-ru-v2/tree/chp9-work-with-input) на данный момент (включая *alert* и *console.log*). Пока что оставлен *input* с *ref*.
