Обработка событий в React: onClick срабатывает при рендере
Пройдите тест, узнайте какой профессии подходите
Быстрый ответ
Для предотвращения автоматического выполнения функции во время рендеринга, убедитесь, что в onClick
вы передаёте ссылку на функцию, а не вызываете саму функцию. В случае инлайн обработки воспользуйтесь стрелочной функцией, либо передайте ссылку на обработчик напрямую, если входные параметры отсутствуют.
// Неправильно: функция будет выполняться сразу после рендеринга!
<button onClick={ringTheBell()} />
// Правильно: функция ожидает клика.
<button onClick={() => ringTheBell()} />
// Если параметры не требуются: оптимальное решение.
<button onClick={ringTheBell} />
Прочитав статью до конца, вы узнаете больше о важных нюансах различия между ссылками на функции и вызовами функций.
Обработка параметров в onClick
Если нужно передать параметры в onClick
, используйте стрелочную функцию или метод .bind
. Выбор за вами, но не забывайте о производительности:
Стрелочная функция:
<button onClick={() => tossTheRing(intoTheVolcano)} />
Привязанная функция:
<button onClick={tossTheRing.bind(this, intoTheVolcano)} />
Обратите внимание: .bind
создаёт новый экземпляр функции каждый раз при рендеринге. Стрелочная функция в этом случае является элегантной альтернативой.
Производительность и предотвращение возможных ошибок
Для избежания ненужных ререндеров определите callback-функции вне метода render в классовых компонентах или вне тела функционального компонента. Вот пример хорошего паттерна с использованием хуков для сохранения стабильных ссылок на функции:
function Task({taskId, taskName}) {
// Преимущества 'useCallback' очевидны!
const removeTask = useCallback(() => {
dontForgetToDelete(taskId);
}, [taskId]);
return <button onClick={removeTask}>Удалить {taskName}</button>;
}
Избегайте следующих распространённых ошибок:
- Определение функции внутри метода render без
useCallback
, что приводит к созданию новой функции при каждом рендере. - Использование
onClick={makeCoffee()}
вместоonClick={() => makeCoffee()}
, что приводит к нежелательному поведению при рендере.
Визуализация
Событие onClick
можно представить как звонок в дверь (🏠):
// Обычный React компонент с кнопкой
function WelcomeGuest() {
// Приглашаем войти!
function ringBell() {
alert('Динь-дон! Добро пожаловать!');
}
return (
<button onClick={ringBell}>Звонок</button>
);
}
Сценарий:
🏠: [Дверь закрыта, звонок не звенит]
При корректном использовании обработчика onClick
:
Звонок (👆): [Дверь остаётся закрытой, звонок звенит только после нажатия]
Но в случае прямого вызова функции:
<button onClick={ringBell()}>Звонок</button>
Будет:
Ошибка в настройке: [Звонок начинает серию звуков 🛎️🛎️🛎️, гости в замешательстве 😕]
Контролируйте динь-дон, пердавая в onClick
ссылку на функцию, вместо её вызова.
Управление this
в классовых компонентах
В классовых компонентах важно правильно управлять контекстом переменной this
. Вот несколько способов, как это можно сделать:
Классовое свойство + стрелочная функция:
class MyButton extends React.Component {
handleClick = () => {
console.log('Мир стало лучше!');
};
render() {
return <button onClick={this.handleClick}>Нажми на меня</button>;
}
}
Привязка в конструкторе:
class MyButton extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Мир стал лучше, как в старые добрые времена.');
}
render() {
return <button onClick={this.handleClick}>Нажми на меня</button>;
}
}
Оба варианта эффективно управляют this
, однако стрелочные функции выглядят более современно и позволяют избежать проблем с производительностью, связанными с необходимостью повторного привязывания функций при каждом рендере.
Пишите достойный и понятный код
Ставьте перед собой цель написать понятный и качественный код:
- Выносите сложные обработчики событий из JSX-выражений, чтобы не перегружать
render
метод. - Применяйте хуки и вспомогательные функции, если логика повторяется между разными обработчиками и компонентами.
- Оформляйте обработчики событий так, чтобы они были доступны, добавляя семантически правильные
aria-
атрибуты к интерактивным элементам.
Полезные материалы
- Handling Events – React — Основы работы с событиями в React.
- EventTarget: addEventListener() method – Web APIs | MDN — Основы работы с событиями в JavaScript.
- Arrow Functions: To bind or not to bind? – Medium — Преимущества использования стрелочных функций и
this
. - React Patterns — Паттерны проектирования в React.
- Binding vs Arrow-function on StackOverflow — Обсуждение
.bind
и стрелочных функций. - JS Event Delegation — Дополнительная информация о делегировании событий.
- RWieruch — Распространённые сложности при работе с JavaScript в контексте React.