Интерфейс это не статичная картинка, но как понять что человек делает с ним?
Привет! Девятый урок и мы подбираемся к настоящему фронтэнду — передней части продукта, так сказать. Не вёрстке (пускай и на Реакте со СК и Роутером), а именно что фронтэнду.
Но для начала — пару обязательных технических знаний.
Если вы уже начали экспериментировать с кодом, то вы, наверное, заметили, что вслепую сложно что-то делать.
Люди с курса по вёрстке знают про дев тулз — специальная утилита браузера, которая показывает дерево элементов и его стили.
Во всех современных браузерах (от Эджа до Хрома и Сафари) есть ДевТулз — инструменты разработчика. Обычно они делятся на кучу вкладок, но нас интересуют две: дерево элементов и консоль.
В консоль можно писать через функцию console.log()
.
Для Хрома и Файрфокса поставьте Реакт ДевТулз, тогда у вас будет доступ ещё и к компонентам с их стейтами и пропами. Удобно.
На ДОМе (DOM, Document Object Model) построена вся работа браузеров. ДОМ, как вы понимаете, это дерево элементов, а Виртуал ДОМ — это технология, работающая с двумя деревами: она сравнивает их между собой и вычленяет маленькие изменения, чтобы экономно обновить ДОМ.
А как, кстати, работать с реальным ДОМом? Это же не магия какая-то, которая доступна только разработчикам библиотек типа Реакта. Веб вообще славится своей открытостью: всем доступно всё.
Работа с ДОМом императивна: вы должны указать "возьми этот элемент и сделай с ним что-нибудь".
Работа с элементами в Реакте декларативна: вы указываете "у этого элемента такие свойства".
Непонятно? Согласен. Давайте разбирать на примерах.
Если человек куда-то кликнул, проскроллил или ещё что-то сделал с интерфейсом, как это отследить? Как поставить хотя бы консоль.лог на это событие?
Событие! Вот как работает интерфейс: на событиях, эвентах. Произошло событие — мы должны его обработать. Как это лучше всего сделать? Наверное, функцией? Вызвать функцию когда произошло событие — это звучит логично.
Так оно и есть: когда происходит эвент, вызывается его хэндлер (обработчик). Хэндлер эвента это функция, в которую приходят аргументы, в этой функции мы делаем что-нибудь с эвентом, хоть консоль.логом выводим.
Судя по МДН, эвентов очень много, но мы будем рассматривать два простейших: клик по элементу и, допустим, фокус по инпуту.
Кстати, если вам интересно, почему я не перевожу термины — чтобы вам было проще с ними работать и гуглить информацию по ним.
Почитывая в прошлом Хабрахабр, я заметил идиотские переводы простейших терминов и не понимал даже как гуглить о чём идёт речь.
Как я и говорил, в браузерном Джсе мы работаем императивно: берём элемент и вешаем на него хэндлер.
<button id="my-button">click me</button>
<script>
// ищем элемент через document.getElementById()
// https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById
const button = document.getElementById("my-button");
// вешаем на него клик
button.addEventListener("click", function(event) {
// при клике выводим в консоль данные эвента
console.log(event);
});
</script>
Скажем так, выглядит не очень удобно. А что если мы сможем указать прямо у элемента атрибут onClick и туда передать функцию?
И мы можем: существуют три вида работы с эвент хэндлерами и addEventListener
как раз один из них.
Какой второй способ? Через атрибут onclick
указать функцию и что в неё придёт:
<button onclick="handleMyButtonClick(event)">
click me
</button>
<script>
function handleMyButtonClick(event) {
console.log(event);
}
</script>
Выглядит уже намного лучше и понятнее, верно? Этот путь находится очень рядом с системой эвентов Реакта.
В примере выше есть несколько проблем:
- непонятный стиль кода вместо camelCase (
onClick
читается легче чемonclick
), - функция зачем-то передаётся как строка,
- функция как будто вызывается прямо в атрибуте, но на деле нет,
- нет системы ошибок: если забыл объявить
handleMyButtonClick
, то пользователь узнает об этом только при клике на кнопку, хотя Еслинт мог бы подсветить что такой функции нет.
Все четыре проблемы решил Реакт через лозунг "явное лучше неявного". С первым пунктом всё понятно, давайте пройдёмся по остальным трём.
Мы знаем, что в Джсхе мы можем выполнять любой Джс-код в {}
: хоть {2+2}
, хоть {getUserInfo(userId)}
. Пропы тоже поддерживают этот синтаксис:
import React from "react";
import styled from "styled-components";
const Button = styled.button`
// умножим размер на 10
// чтобы получить размер паддингов
padding: ${props => p.size * 10}px;
`;
<Button size={2 + 2}>Button of 4</Button>;
По этой причине мы на клик по кнопке можем передать функцию.
// Button.js
function handleClick(event) {
console.log(event);
}
<button onClick={handleClick(event)}>click me</button>;
Но стоп! Стоп-стоп-стоп! Если мы так сделаем, откроем страницу, то даже без клика мы получим консоль.лог в браузере.
iframe: https://codesandbox.io/embed/k3kp4l0p8v
Как так?
В проп должна передаться ссылка на функцию, а не её вызов.
// неправильно — вызовется сразу как компонент станет доступен
<button onClick={handleClick(event)}>click me</button>
// правильно — при клике на элемент вызовется эта функция
<button onClick={handleClick}>click me</button>
Как понять, что в Реакте приходит на этом
onClick
? Читайте официальную документацию: Handling Events иSyntheticEvent
. Первая статья рассказывает о работе эвентов, вторая — о специальном типе данныхSyntheticEvent
, который отличается от дефолтного объекта браузера, который приходит в хэндлере эвента.
Четвёртая проблема, как вы понимаете, тоже решена: мы в Джсхе передаём обычный джаваскрипт, поэтому если наша функция не будет существовать, то Еслинт об этом расскажет — либо в редакторе (если вы настроили интеграцию), либо при сборке проекта (об этом в будущем).
В этом уроке мы не только разобрались с системой эвентов в Реакте и поняли чем она мощна, но и поработали с браузерными ДОМом и эвентами.
А говорят, что не нужно начинать знакомство с фронтэндом через популярные библиотеки и фреймворки! Как видите, мы учимся чистому Джсу как раз потому что Реакт не использует свой птичий язык шаблонов и заставляет работать с чистым Джсом.
Кстати, как попрактиковаться в этих знаниях? Отловили мы эвент, дальше-то что? Узнаете в следующем уроке — там мы будем соединять стейт и эвенты.