Обработка событий элементов в React во многом похожа на обработку событий элементов DOM с помощью обычного JavaScript. В то же время есть небольшие отличия:
События в React используют camelCase (в стандартном html "onclick", в React - "onClick")
В JSX в обработчик события передается функция компонента, а не строка
В React можно применять разные способы определения и вызова событий. Возьмем простейшую задачу - обработка нажатия кнопки.
Использование событий в функциональных компонентах несколько проще. Так, перепишем предыдущий компонент в функциональном стиле:
function ClickButton(props) { function press(){ alert("Hello React!") } return <button onClick={press}>Click</button>; }
Здесь используется событие нажатия кнопки, которое задается через атрибут onClick (не onclick
).
Этому атрибуту в качестве обработчика события передавалась функция press
, которая определена в функции компонента.
И при нажатии на кнопку будет вызываться функция press
, которая с помощью функции alert
отображает окно с некоторым уведомлением.
Использование функционального компонента:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="app"></div> <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <script type="text/babel"> function ClickButton(props) { function press(){ alert("Hello React!") } return <button onClick={press}>Click</button>; } ReactDOM.createRoot( document.getElementById("app") ) .render( <ClickButton /> ); </script> </body> </html>
При определении событий в классах-компонентах распространенный подход заключается в установке привязки события в конструкторе компонента:
class ClickButton extends React.Component { constructor(props) { super(props); this.press = this.press.bind(this); } press(){ console.log(this); alert("Hello React!") } render() { return <button onClick={this.press}>Click</button>; } }
Главная сложность при использовании событий в компонентах-классах - это работа с ключевым словом this, которое указывает на текущий
объект, в данном случае компонент. По умолчанию в функцию обработчика не передается текущий объект, поэтому this
будет иметь значение undefined
. И ни к каким свойствам и методам компонента
через this мы обратиться не сможем. И чтобы в метод press корректно передавалась ссылка на текущий объект через this, в конструкторе класса
прописывается вызов:
this.press = this.press.bind(this);
И также стоит отметить, что при определении конструктора компонента в нем должен вызываться конструктор базового класса, в который передается объект props
.
Использование класса-компонента на веб-странице аналогично:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="app"></div> <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <script type="text/babel"> class ClickButton extends React.Component { constructor(props) { super(props); this.press = this.press.bind(this); } press(){ console.log(this); alert("Hello React!") } render() { return <button onClick={this.press}>Click</button>; } } ReactDOM.createRoot( document.getElementById("app") ) .render( <ClickButton /> ); </script> </body> </html>
Однако есть и другие способы определения и вызова события. Например, определение обработчика в виде публичного поля компонента, которое указывает на стрелочную функцию.
class ClickButton extends React.Component { press = () => { console.log(this); alert("Hello React!") } render() { return <button onClick={this.press}>Click</button>; } }
Либо мы можем определить функцию обработчика события как обычный метод класса, а вызывать с помощью стрелочной функции:
class ClickButton extends React.Component { press(){ console.log(this); alert("Hello React!"); } render() { return <button onClick={() => this.press()}>Click</button>; } }
Однако в случае с использованием стрелочной функции есть вероятность столкнуться с проблемой производительности, если функция обработчика передается через свойства props вложенным компонентам. Так как обработчик события будет создаваться каждый раз заново при каждом рендеринге компонента, что может привести к дополнительному повторному рендерингу вложенных компонентов, без которого можно было бы обойтись. Поэтому использование конструктора является более предпочтительной практикой.
React использует концепцию SyntheticEvent - специальных объектов, которые представляют собой обертки для объектов событий, передаваемых в функцию события. И используя такой объект, мы можем получить в обработчике события всю информацию о событии:
function ClickButton(props) { function press(e){ console.log(e); // выводим информацию о событии alert("Hello React!") } return <button onClick={press}>Click</button>; }
Параметр e - это и есть информация о событии, которая передается в обработчик системой и которую мы можем использовать при обработке.
Аналогичный пример для класса-компонента:
class ClickButton extends React.Component { constructor(props) { super(props); this.press = this.press.bind(this); } press(e){ console.log(e); // выводим информацию о событии alert("Hello React!") } render() { return <button onClick={this.press}>Click</button>; } }
Если необходимо передать в обработчик события некоторые аргументы, то в этом случае можно вызвать обработчик через стрелочную функцию:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>METANIT.COM</title> </head> <body> <div id="app"></div> <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <script type="text/babel"> function PrintButton(props) { function print(name, age){ console.log(`Name ${name} Age: ${age}`); } return <div> <button onClick={() => print("Bob", 23)}>Print Bob</button> <button onClick={() => print("Tom", 36)}>Print Tom</button> </div>; } ReactDOM.createRoot( document.getElementById("app") ) .render( <PrintButton /> ); </script> </body> </html>
Аналогичный пример для класса-компонента:
class PrintButton extends React.Component { constructor(props) { super(props); this.print = this.print.bind(this); } print(name, age){ console.log(`Name ${name} Age: ${age}`); } render() { return <div> <button onClick={() => this.print("Bob", 23)}>Print Bob</button> <button onClick={() => this.print("Tom", 36)}>Print Tom</button> </div>; } }