Особенности обработки native events в React.js

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

В данной статье рассматриваются особенности, которые связаны с обработкой нативных событий (native events) в React-приложениях. Существует проблема частичной потери контекста функционального компонента при обработке нативных событий, которые навешиваются на элементы с помощью глобальных объектов document, window или через ссылки (refs). В статье рассматривается данная проблема и предлагается способ её решения (один из вариантов).

Мотивация

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

Я достаточно часто сталкиваюсь с необходимостью обрабатывать нативные события и до определённого времени делал это не правильно, что приводило к ошибкам. На удивление я нашёл не много информации, которая помогает разобраться с такими ошибками и ещё меньше - объясняющие их природу и почему всё работает так, а не иначе.

Эта статья призвана повысить осведомлённость читателя о существовании некоторых проблем, которые могут возникнуть при обработке нативных событий, и показать необходимость в учитывании их особенностей.

Используемая версия React.js, Node.js и других зависимостей

Версия React.js - 18.2.0.

Версия Node.js - 16.20.2.

Подробный список dependencies тестового проекта:

"dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
}

Синтетические и нативные события

Для начала разберёмся что вообще такое синтетическое событие в контексте React.js.

Синтетическое событие - это кроссбраузерная обёртка SyntheticEvent, которая предназначена для унификации работы с событиями в DOM-дереве.

Синтетические события являются крайне полезными при разработке веб-приложений, которые ориентированы на множество браузеров (поддержка кроссбраузерности). Благодаря им гарантируется (насколько это возможно), что ваше веб-приложение будет одинаково обрабатывать события в Opera, Google, Safari, Mozilla Firefox, Yandex и других популярных браузерах. Также они позволяют избежать утечек памяти благодаря автоматическому управлению памятью (чего нет при обработке нативных событий), что делает синтетические события ещё более полезными.

Какие события можно считать синтетическими? В общем-то все, которые поддерживаются на данный момент и содержатся (важно) в определении DOM-дерева функционального или классового компонента (в JSX-коде).

То есть, все события, которые обрабатываются в следующем программном коде, являются синтетическими:

// ...

return (
    <>
      <div
        className="wrapper"
        onMouseLeave={(e) => {
          // Обработка синтетического события ...
        }}
      >
        <div
          id="containerId"
          className="container"
          onDoubleClick={(e) => {
            // Обработка синтетического события ...
          }}
        >
          <span
            onClick={(e) => {
              // Обработка синтетического события ...
            }}
          >{value}</span>
        </div>
      </div>
    </>
);

// ...

Это также не трудно понять по объекту event, который возвращается внутрь функции обработчика синтетических событий. Как правило данный объект всегда имеет класс SyntheticBaseEvent.

Пояснения по объекту event

Объект event - это первый параметр, который возвращается в функцию-обработчик какого-либо события (нативного, синтетического или, даже, кастомного).

Вот код, который описывает получение и вывод этого объекта на экран (на выходе получим объект класса SyntheticBaseEvent):

// ...

<div className="wrapper">
    <div
        id="containerId"
        className="container"
        onClick={(e) => {
          // Вывод объекта класса SyntheticBaseEvent
          console.log(e);
        }}
      >
       <span>{value}</span>
    </div>
</div>

// ...

В обработчик обычно передаётся более короткая форма параметра event - e.

Рисунок 1 - "Все мы разные, но служим одной цели - обработке событий!"
Рисунок 1 - "Все мы разные, но служим одной цели - обработке событий!"

С синтетическими событиями разобрались. Что же такое нативные события?

В контексте данной статьи к нативным событиям относятся все события, которые не являются синтетическими, но при этом связаны с обработкой события в DOM-дереве.

Вообще, я думаю, что чистых нативных событий сейчас уже нет, поскольку каждый браузер может обрабатывать их по своему и модифицировать цепочку прототипов объекта event как угодно. В каком-то браузере может быть параметр, который не поддерживается в другом (пример из вакуума не лишённый смысла). В общем, что считать чистым нативным событием в контексте React - вопрос философский и я предлагаю читателю поразмышлять на данную тему.

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

Нативные события, в основном, обрабатываются с помощью стандартного метода addEventListener, который вызывается у глобальных объектов window, document или не пустых (not null) ссылок (refs).

Вот пример обработки нативных событий:

// ...

useEffect(() => {
    const container = document.getElementById("containerId");

    // Навешиваем обработчики на нативные события
    container.addEventListener("click", mouseMoveHandler);
    window.addEventListener("mousedown", mouseDownHandler);

    return () => {
      // Удаляем обработчики с нативных событий, 
      // иначе они будут работать и после выхода со страницы
      window.removeEventListener("mousedown", mouseDownHandler);

      // Кроме данной строки - здесь можно и не удалять, 
      // ведь если нет DOM-элемента, то и нет проблемы :)
      container.removeEventListener("click", mouseMoveHandler);
    };
  }, []);

// ...

В общем-то с разницей между нативными (в контексте данной статьи) и синтетическими событиями разобрались.

Кстати, стоит упомянуть о том, что при обработке синтетических событий можно получить объект event, который мы бы получили при обработке нативного события. Для этого можно обратиться к атрибуту nativeEvent в объекте event.

Проблема при обработке нативного события

Далее следует ввести понятие контекста React-компонента.

Контекст React-компонента (в рамках данной статьи) - это все функции, состояния, refs, переменные и константы, которые определены в рамках одного React-компонента.

Например, контекстом React-компонента (далее - контекст или контекст компонента) является всё, что определено в следующем программном коде:

// ...

const [value, setValue] = useState(0);
const [state, setState] = useState({
    arr: []
});
const timer = useRef(null);
const handler = (e) => {
    console.log(e);
}

// ...

или:

// ...

constructor(props) {
      super(props);

      this.state = {
          value: 0
      };

      this.timer = React.createRef(null);
      this.mouseMoveHandler = this.mouseMoveHandler.bind(this);
}

// ...

В общем, можно воспринимать контекст, как окружение компонента. В классовом компоненте его роль принимает объект this.

Теперь перейдём к проблеме.

В контексте компонента очень часто разработчики определяют и используют состояния (useState, this.state), которые играют если не ключевую роль, то очень важную при функционировании и разработке пользовательского интерфейса.

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

Рисунок 2 - Это рандомайзер и он генерирует 						</div>

Источник: https://habr.com/ru/articles/810205/<br/><br/>




<div style=

Интересные статьи

Интересные статьи

Привет! Это tech-команда СберМаркет. Сегодня кто-то празднует день влюбленных, а для нас с вами это ещё и профессиональный праздник — день компьютерщика. Если совместить два повода, получим день ...
Энкодер - это устройство преобразования механического перемещения или угловых изменений положения в цифровой сигнал. В статье рассматривается самый популярный в DIY сообществе инкрементальный энкодер...
Автор нашей новой переводной статьи утверждает, что Knative — лучшее, что только могли придумать во Вселенной! Вы согласны? Если вы уже используете Kubernetes, то, вероятно, слышали о бе...
Чтобы быстро решать вопросы пользователей без вмешательства человека, эффективный чат-бот требует огромного количества обучающих данных. Однако основное узкое место в разработке чат-бота ...
Продолжаем лаконичную интерпретацию официальной документации Flutter в формате «вопрос-ответ». Вот уже 3-я часть, и она в большей степени будет полезна React Native-разработчикам. В данной интерп...