Spec-Zone.ru › React

Реагирование на события

React позволяет добавлять обработчики событий к вашему JSX. Обработчики событий — это ваши собственные функции, которые будут вызываться в ответ на взаимодействия, такие как щелчки, наведение указателя, фокусировка элементов формы и т. д.

Вы узнаете

  • Разные способы написания обработчика событий
  • Как передавать логику обработки событий из родительского компонента
  • Как события распространяются и как их остановить

Добавление обработчиков событий

Чтобы добавить обработчик события, вы сначала определите функцию, а затем передадите её в качестве свойства соответствующему тегу JSX. Например, вот кнопка, которая пока ничего не делает:

export default function Button() {
  return (
    <button>
      I don't do anything
    </button>
  );
}

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

  1. Объявите функцию с именем handleClick внутри вашего Button компонента.
  2. Реализуйте логику внутри этой функции (используйте alert для отображения сообщения).
  3. Добавьте onClick={handleClick} к <button> JSX.
export default function Button() {
  function handleClick() {
    alert('You clicked me!');
  }

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

Вы определили функцию handleClick и затем передали её в качестве свойства в <button>. handleClick является обработчиком события. Функции обработчиков событий:

  • Обычно определяются внутри ваших компонентов.
  • Имеют имена, начинающиеся с handle, за которым следует имя события.

По соглашению, обработчики событий обычно называют handle за которым следует имя события. Вы часто увидите onClick={handleClick}, onMouseEnter={handleMouseEnter} и так далее.

В качестве альтернативы, вы можете определить обработчик события непосредственно в JSX:

<button onClick={function handleClick() {
  alert('You clicked me!');
}}>

Или, более кратко, с помощью стрелочной функции:

<button onClick={() => {
  alert('You clicked me!');
}}>

Все эти стили эквивалентны. Встроенные обработчики событий удобны для коротких функций.

Возможная ошибка

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

передача функции (правильно) вызов функции (неправильно)
<button onClick={handleClick}> <button onClick={handleClick()}>

Разница тонкая. В первом примере функция handleClick передаётся как onClick обработчик события. Это сообщает React запомнить её и вызвать только при нажатии пользователем на кнопку.

Во втором примере, () в конце handleClick() вызывает функцию немедленно во время рендеринга, без каких-либо щелчков. Это происходит потому, что JavaScript внутри JSX { и } выполняется сразу.

При написании кода в строке та же ошибка проявляется по-другому:

передача функции (правильно) вызов функции (неправильно)
<button onClick={() => alert('...')}> <button onClick={alert('...')}>

Передача кода в строке таким образом не будет срабатывать при нажатии — он срабатывает каждый раз при рендеринге компонента:

// This alert fires when the component renders, not when clicked!
<button onClick={alert('You clicked me!')}>

Если вы хотите определить свой обработчик события в строке, оберните его в анонимную функцию следующим образом:

<button onClick={() => alert('You clicked me!')}>

Вместо выполнения кода внутри при каждом рендеринге, это создаёт функцию, которая будет вызвана позже.

В обоих случаях, нужно передавать функцию:

  • <button onClick={handleClick}> передаёт функцию handleClick.
  • <button onClick={() => alert('...')}> передаёт функцию () => alert('...').

Подробнее о стрелочных функциях.

Чтение свойств в обработчиках событий

Поскольку обработчики событий объявляются внутри компонента, у них есть доступ к свойствам компонента. Вот кнопка, которая при нажатии отображает всплывающее окно с её свойством message.

function AlertButton({ message, children }) {
  return (
    <button onClick={() => alert(message)}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div>
      <AlertButton message="Playing!">
        Play Movie
      </AlertButton>
      <AlertButton message="Uploading!">
        Upload Image
      </AlertButton>
    </div>
  );
}

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

Передача обработчиков событий как свойств

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

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

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

function PlayButton({ movieName }) {
  function handlePlayClick() {
    alert(`Playing ${movieName}!`);
  }

  return (
    <Button onClick={handlePlayClick}>
      Play "{movieName}"
    </Button>
  );
}

function UploadButton() {
  return (
    <Button onClick={() => alert('Uploading!')}>
      Upload Image
    </Button>
  );
}

export default function Toolbar() {
  return (
    <div>
      <PlayButton movieName="Kiki's Delivery Service" />
      <UploadButton />
    </div>
  );
}

Здесь, компонент Toolbar отображает PlayButton и UploadButton.

  • PlayButton передаёт handlePlayClick как свойство onClick в Button внутри.
  • UploadButton передаёт () => alert('Uploading!') как свойство onClick в Button внутри.

Наконец, ваш компонент Button принимает свойство, называемое onClick. Он передаёт это свойство непосредственно встроенному браузерному <button> с помощью onClick={onClick}. Это сообщает React вызвать переданную функцию при нажатии.

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

Наименование свойств обработчиков событий

Встроенные компоненты, такие как <button> и <div>, поддерживают только имена событий браузера, такие как onClick. Однако, когда вы создаёте свои собственные компоненты, вы можете называть их свойства обработчиков событий как угодно.

По соглашению, свойства обработчиков событий должны начинаться с on, за которым следует заглавная буква.

Например, свойство Button компонента onClick могло быть названо onSmash:

function Button({ onSmash, children }) {
  return (
    <button onClick={onSmash}>
      {children}
    </button>
  );
}

export default function App() {
  return (
    <div>
      <Button onSmash={() => alert('Playing!')}>
        Play Movie
      </Button>
      <Button onSmash={() => alert('Uploading!')}>
        Upload Image
      </Button>
    </div>
  );
}

В этом примере, <button onClick={onSmash}> показывает, что браузерному <button> (в нижнем регистре) всё ещё необходимо свойство с именем onClick, но имя свойства, полученное вашим пользовательским компонентом Button, зависит от вас!

Если ваш компонент поддерживает несколько взаимодействий, вы можете называть свойства обработчиков событий для понятных для приложения концепций. Например, этот компонент Toolbar получает обработчики событий onPlayMovie и onUploadImage:

export default function App() {
  return (
    <Toolbar
      onPlayMovie={() => alert('Playing!')}
      onUploadImage={() => alert('Uploading!')}
    />
  );
}

function Toolbar({ onPlayMovie, onUploadImage }) {
  return (
    <div>
      <Button onClick={onPlayMovie}>
        Play Movie
      </Button>
      <Button onClick={onUploadImage}>
        Upload Image
      </Button>
    </div>
  );
}

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

Обратите внимание, как компонент App не нуждается в том, чтобы знать, что Toolbar будет делать с onPlayMovie или onUploadImage. Это реализация детали компонента Toolbar. Здесь Toolbar передаёт их как обработчики onClick своим Button, но позже он также может запустить их при нажатии клавиши. Называние свойств приложения, таких как onPlayMovie, обеспечивает гибкость при изменении способа их использования в будущем.

Примечание

Убедитесь, что вы используете соответствующие теги HTML для своих обработчиков событий. Например, для обработки нажатий используйте <button onClick={handleClick}> вместо <div onClick={handleClick}>. Использование реального браузерного <button> позволяет использовать встроенное поведение браузера, такое как навигация по клавиатуре. Если вам не нравится стандартная стилизация браузера кнопки и вы хотите сделать её похожей на ссылку или другой элемент пользовательского интерфейса, вы можете сделать это с помощью CSS. Узнайте больше о написании доступной разметки.

Распространение событий

Обработчики событий также будут обрабатывать события, которые могут быть у любого дочернего компонента вашего компонента. Мы говорим, что событие «всплывает» или «распространяется» вверх по дереву: оно начинается с места, где произошло событие, а затем идёт вверх по дереву.

Этот <div> содержит две кнопки. У <div> и каждой кнопки есть свои обработчики onClick событий. Какие обработчики, по вашему мнению, будут срабатывать при нажатии на кнопку?

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <button onClick={() => alert('Playing!')}>
        Play Movie
      </button>
      <button onClick={() => alert('Uploading!')}>
        Upload Image
      </button>
    </div>
  );
}

Если вы нажмёте на любую кнопку, её обработчик onClick будет выполнен первым, а затем обработчик родительского <div>’s onClick. Поэтому появятся два сообщения. Если вы нажмёте на сам инструмент, выполнится только обработчик родительского <div>’s onClick.

Возможная ошибка

Все события распространяются в React, кроме onScroll, которое работает только с тегом JSX, к которому оно прикреплено.

Остановка распространения

Обработчики событий получают объект события в качестве единственного аргумента. По соглашению, он обычно называется e, что означает «событие». Вы можете использовать этот объект для чтения информации о событии.

Этот объект события также позволяет остановить распространение. Если вы хотите предотвратить достижение события родительскими компонентами, вам нужно вызвать e.stopPropagation() следующим образом, как это делает компонент Button.

function Button({ onClick, children }) {
  return (
    <button onClick={e => {
      e.stopPropagation();
      onClick();
    }}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <Button onClick={() => alert('Playing!')}>
        Play Movie
      </Button>
      <Button onClick={() => alert('Uploading!')}>
        Upload Image
      </Button>
    </div>
  );
}

При нажатии на кнопку:

  1. React вызывает обработчик onClick свойства, переданного <button>.
  2. Этот обработчик, определённый в Button, выполняет следующее:
    • Вызывает e.stopPropagation(), предотвращая дальнейшее распространение события.
    • Вызывает функцию onClick, которая является свойством, переданным компонентом Toolbar.
  3. Эта функция, определённая в компоненте Toolbar , отображает собственное всплывающее окно кнопки.
  4. Поскольку распространение было остановлено, обработчик родительского <div>’s onClick не выполняется.

В результате e.stopPropagation(), нажатие на кнопки теперь отображает только одно всплывающее окно (из <button>) вместо двух (из <button> и родительской панели инструментов <div>). Нажатие на кнопку — это не то же самое, что нажатие на окружающую панель инструментов, поэтому остановка распространения имеет смысл для этого пользовательского интерфейса.

Глубокий анализ

События фазы захвата

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

<div onClickCapture={() => { /* this runs first */ }}>
  <button onClick={e => e.stopPropagation()} />
  <button onClick={e => e.stopPropagation()} />
</div>

Каждое событие распространяется в трёх фазах:

  1. Оно проходит вниз, вызывая все onClickCapture обработчики.
  2. Оно выполняет обработчик нажатия на элемент onClick.
  3. Оно перемещается вверх, вызывая все onClick обработчики.

События захвата полезны для кода маршрутизаторов или аналитики, но в прикладном коде вы их, вероятно, не будете использовать.

Передача обработчиков в качестве альтернативы распространению

Обратите внимание, как этот обработчик клика выполняет строку кода и затем вызывает onClick prop, переданный родителем:

function Button({ onClick, children }) {
  return (
    <button onClick={e => {
      e.stopPropagation();
      onClick();
    }}>
      {children}
    </button>
  );
}

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

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

Предотвращение стандартного поведения

Некоторые события браузера имеют связанное с ними стандартное поведение. Например, событие <form> отправки, которое происходит, когда нажимается кнопка внутри него, по умолчанию перезагружает всю страницу:

export default function Signup() {
  return (
    <form onSubmit={() => alert('Submitting!')}>
      <input />
      <button>Send</button>
    </form>
  );
}

Вы можете вызвать e.preventDefault() на объекте события, чтобы остановить это:

export default function Signup() {
  return (
    <form onSubmit={e => {
      e.preventDefault();
      alert('Submitting!');
    }}>
      <input />
      <button>Send</button>
    </form>
  );
}

Не путайте e.stopPropagation() и e.preventDefault(). Они оба полезны, но не связаны:

  • e.stopPropagation() останавливает срабатывание обработчиков событий, прикреплённых к тегам выше.
  • e.preventDefault() предотвращает стандартное поведение браузера для нескольких событий, которые его имеют.

Могут ли обработчики событий иметь побочные эффекты?

Безусловно! Обработчики событий — лучшее место для побочных эффектов.

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

Резюме

  • Вы можете обрабатывать события, передавая функцию в качестве параметра элементу, например, <button>.
  • Обработчики событий должны передаваться, а не вызываться! onClick={handleClick}, а не onClick={handleClick()}.
  • Вы можете определить функцию обработчика события отдельно или в строчном виде.
  • Обработчики событий определяются внутри компонента, поэтому они могут получить доступ к параметрам.
  • Вы можете объявить обработчик события в родительском элементе и передать его в качестве параметра дочернему.
  • Вы можете определять свои собственные параметры обработчика событий с именами, специфичными для приложения.
  • События распространяются вверх. Вызовите e.stopPropagation() для первого аргумента, чтобы предотвратить это.
  • События могут иметь нежелательное стандартное поведение браузера. Вызовите e.preventDefault() для предотвращения этого.
  • Явное вызов параметра обработчика событий из обработчика дочернего элемента — хорошая альтернатива распространению.

Попробуйте выполнить некоторые задания

Задание 1 из 2:
Исправить обработчик события

Нажатие этой кнопки должно переключать фоновый цвет страницы с белого на чёрный. Однако ничего не происходит при нажатии. Исправьте проблему. (Не беспокойтесь о логике внутри handleClick — эта часть в порядке.)

export default function LightSwitch() {
  function handleClick() {
    let bodyStyle = document.body.style;
    if (bodyStyle.backgroundColor === 'black') {
      bodyStyle.backgroundColor = 'white';
    } else {
      bodyStyle.backgroundColor = 'black';
    }
  }

  return (
    <button onClick={handleClick()}>
      Toggle the lights
    </button>
  );
}

© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://react.dev/learn/responding-to-events

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API