Работа с инпутами в React


Работа с инпутами в React происходит с помощью стейтов. Каждому инпуту назначается свой стейт, содержащий в себе value инпута.

Давайте посмотрим на примере. Пусть у нас есть инпут:

function App() {
return <div>
<input />
</div>;
}
Пусть также у нас есть стейт:

function App() {
const [value, setValue] = useState('text');

return <div>
<input />
</div>;
}
Давайте к атрибуту value инпута привяжем нас стейт:

function App() {
const [value, setValue] = useState('text');

return <div>
<input value={value} />
</div>;
}
В таком случае получится, что при изменении стейта, реактивно поменяется и текст инпута.

Это, однако, дает интересный побочный эффект: теперь при запуске кода в браузере в инпуте невозможно поменять текст! Почему: потому что при вводе текста в инпут не меняется стейт value, соответственно, и текст в инпуте не должен меняться.

Попробуйте сами. Скопируйте мой код и запустите у себя. Попробуйте поизменять текст в инпуте - у вас ничего не получится. Откройте консоль браузера - вы увидите в ней предупреждение React. Это предупреждение указывает нам, что мы привязали стейт к инпуту, но тем самым заблокировали инпут.

Давайте сделаем так, чтобы в наш инпут можно было вводить текст. Для этого нужно сделать так, чтобы при вводе изменялся наш стейт на текущее значение инпута.

Для начала для этого нужно навесить на инпут событие onChange:

function App() {
const [value, setValue] = useState('text');

return <div>
<input value={value} onChange={handleChange} />
</div>;
}
Данное событие в React ведет себя по-другому по сравнению с чистым JS. В React оно срабатывает сразу же по изменению инпута. То есть при вводе или удалении символа.

Давайте теперь добавим обработчик нашего события:

function App() {
const [value, setValue] = useState('text');

function handleChange() {

}

return <div>
<input value={value} onChange={handleChange} />
</div>;
}
В этом обработчике мы должны прочитать текущий текст инпута и установить его в стейт с помощью функции setValue.

Проблема в том, что this данной функции не будет указывать на наш инпут - такова особенность React. Чтобы получить элемент, в котором случилось событие, нам необходимо использовать event.target:

function App() {
const [value, setValue] = useState('text');

function handleChange(event) {
console.log(event.target); // ссылка на DOM элемент инпута
}

return <div>
<input value={value} onChange={handleChange} />
</div>;
}
Выведем с помощью event.target текущий текст инпута:

function App() {
const [value, setValue] = useState('text');

function handleChange(event) {
console.log(event.target.value); // текущий текст инпута
}

return <div>
<input value={value} onChange={handleChange} />
</div>;
}
А теперь запишем текст инпута в наш стейт:

function App() {
const [value, setValue] = useState('text');

function handleChange(event) {
setValue(event.target.value);
}

return <div>
<input value={value} onChange={handleChange} />
</div>;
}
Теперь мы сможем вводить текст в инпут. При этом стейт value всегда будет содержать текущий текст инпута.

Мы можем легко убедится в этом. Выведем содержимое нашего текста в абзац. В этом случае при вводе текста в инпут введенный текст будет автоматически появляться в абзаце:

function App() {
const [value, setValue] = useState('');

function handleChange(event) {
setValue(event.target.value);
}

return <div>
<input value={value} onChange={handleChange} />
<p>text: {value}</p>
</div>;
}
Можем переписать на более компактный вариант с анонимной стрелочной функцией:

function App() {
const [value, setValue] = useState('');

return <div>
<input value={value} onChange={event => setValue(event.target.value)} />
<p>text: {value}</p>
</div>;
}
Таким образом, для работы любого инпута нам нужно следующее: создать стейт для этого инпута, привязать стейт к атрибуту value инпута, навесить событие onChange на инпут, в обработчике события менять стейт инпута на его текст.

Данные операции нужно будет проводить с каждым инпутом. То есть, если у вас два инпута, то у вас будет два стейта и две функции-обработчика события onChange.

Изменение данных при выводе
Пусть мы будем вводить в инпут число. Давайте сделаем так, чтобы по мере ввода числа в инпут в абзац выводился квадрат вводимого числа:

function App() {
const [value, setValue] = useState(0);

function handleChange(event) {
setValue(event.target.value);
}

return <div>
<input value={value} onChange={handleChange} />
<p>{value ** 2}</p>
</div>;
}

Используем функцию
Не обязательно совершать некие операции над стейтом прямо на выводе. Можно воспользоваться функцией:

function square(num) {
return num ** 2;
}

function App() {
const [value, setValue] = useState(0);

function handleChange(event) {
setValue(event.target.value);
}

return <div>
<input value={value} onChange={handleChange} />
<p>{square(value)}</p>
</div>;
}

Несколько инпутов
Пусть у нас есть два инпута, в которые будут вводится числа. Давайте сделаем так, чтобы по мере ввода в абзац выводилась сумма двух инпутов:

function App() {
const [value1, setValue1] = useState(0);
const [value2, setValue2] = useState(0);

function handleChange1(event) {
setValue1(+event.target.value);
}

function handleChange2(event) {
setValue2(+event.target.value);
}

return <div>
<input value={value1} onChange={handleChange1} />
<input value={value2} onChange={handleChange2} />
<p>result: {value1 + value2}</p>
</div>
}