React Events
Tags: react, javascript, webdev
Published: 2020-07-04 08:02:27

Hello Everyone 👋👋👋

In this blog, we are going to talk about React Events, How to attach event handlers to components in React, **this** keyword, the problem with **this** keyword, How to solve that problem and much more.

---

So let's talk about **Events**, just like in HTML we can also handle user events in React and perform some actions based on them. 

Just there are few changes in how we use them-

* In React, events are written in camelCase :

**onClick** instead of **onclick**
<br />
* Also In JSX you can pass function as an event handler rather than a string as in HTML :

**onClick={generate}** instead of **onclick="generate()"**

---

Using special reserved attributes we can attach event handlers to HTML elements in React.

Let's have a look at some examples of Event Attributes :

* Mouse events : **onClick**, **onMouseOver**, etc
* Form events : **onSubmit**, etc
* Keyboard events: **onKeyUp**, **onKeyDown**, etc

Have a look at the whole list of [supported events](https://reactjs.org/docs/events.html#supported-events).
<br>
An example of how to use event handlers :

```react
class Bakery extends React.Component {
  bake() {
    alert("The Cake is Baked!");
  }

  render() {
    return (
      <button onClick={this.bake}>Bake the Cake!</button>
    );
  }
}

ReactDOM.render(<Bakery />, document.getElementById('root'));
```
 
---

We use event handlers independently like for alert or console.log, like in the above example, therefore, we have not used keyword **this** till now but when we have to set state, access prop or just to access the state, etc then we have to reference keyword **this** like **this.setState** or **this.props** and many more.

But when we use keyword **this** inside a method we will lose the **this** context.

And the keyword **this** used inside the methods will return **undefined** and will generate an error :

```react
class Bakery extends React.Component {
  constructor(props){
    super(props);
  }

  bake() {
    console.log(this); // 'this' is undefined
  }

  render() {
    return (
      <button onClick={this.bake}>Bake the Cake!</button>
    );
  }
}

ReactDOM.render(<Bakery />, document.getElementById('root'));
```

<br />
We have to make sure that the keyword **this** refers to the component itself, the individual instance of the component.

For this, we have to have to bind **this** to the component instance.

---

Ways to bind **this** -
<br />
* Use bind inline :

```react
class Bakery extends React.Component {
  constructor(props){
    super(props);
  }

  bake() {
    console.log(this); // "this" refers to the component object
  }

  render() {
    return (
      <button onClick={this.bake.bind(this)}>Bake the Cake!</button> // binding inline
    );
  }
}

ReactDOM.render(<Bakery />, document.getElementById('root'));
```
<br />
The pros of using binding inline is that it is super easy to use but it has some cons which are if we need to pass it to multiple components we have to bind it multiple times and on every render a new function is created.

So, we have a better way of binding **this** but before that let's have a look on a way through which we can bind **this** without the use of the word *bind* -
<br />

* Using *arrow* function :

```react
class Bakery extends React.Component {
  constructor(props){
    super(props);
  }

  bake() {
    console.log(this); // "this" refers to the component object
  }

  render() {
    return (
      <button onClick={() => this.bake()}>Bake the Cake!</button> // binding using arrow function
    );
  }
}

ReactDOM.render(<Bakery />, document.getElementById('root'));
```
<br />
But this also comes with the same set of cons as inline binding so now let's finally look at the better approach -
 <br />
* Method binding in the constructor :

```react
class Bakery extends React.Component {
  constructor(props) {
    super(props);
    this.bake = this.bake.bind(this); // binding in the constructor
  }

  bake() {
    console.log(this); // "this" refers to the component object
  }

  render() {
    return (
      <button onClick={this.bake}>Bake the Cake!</button>
    );
  }
}

ReactDOM.render(<Bakery />, document.getElementById('root'));
```
<br />
The pros are that we only need to bind once, it is more performant and the cons is simply the lengthy syntax. 

There are also some other ways to bind but I found this approach the most descriptive and efficient.

---

Till now we have seen simple event handlers which call a method but what if we want to pass some data? Let's look at how to bind with arguments.
<br />
Lets try and pass something like we usually do :


```react
class Bakery extends React.Component {
  constructor(props) {
    super(props);
  }

  bake(e) {
    alert(e); // it will execute immediately
  }

  render() {
    const e = "Baked!";
    return <button onClick={this.bake(e)}>Bake the Cake!</button>;
  }
}

ReactDOM.render(<Bakery />, document.getElementById('root'));
```

But this will not work as we want. And the function will be executed immediately. So to solve this we have to bind it. There are a few ways for doing the same :
<br />
* Binding the value of **this** using **.bind** :

```react
class Bakery extends React.Component {
  constructor(props) {
    super(props);
  }

  bake(e) {
    alert(e); // it will execute after clicking the button
  }

  render() {
    const e = "Baked!";
    return <button onClick={this.bake.bind(this, e)}>Bake the Cake!</button>; // passing argument using .bind
  }
}

ReactDOM.render(<Bakery />, document.getElementById('root'));
```

Here firstly we will bind and then pass the data as the second argument. And it will work as we want, after clicking the button.
<br/>
* Using *arrow* function :

```react
class Bakery extends React.Component {
  constructor(props) {
    super(props);
  }

  bake(e) {
    alert(e); // it will execute after clicking the button
  }

  render() {
    const e = "Baked!";
    return <button onClick={() => this.bake(e)}>Bake the Cake!</button>; // passing argument using arrow function
  }
}

ReactDOM.render(<Bakery />, document.getElementById('root'));
```

And it will work fine too. But both the cases have a drawback as discussed earlier after each click they will create a new function.
<br />
To solve this we have an approach that I'll discuss in the next blog.

---

Thanks for reading 🙏

Any feedback appreciated 😁

Say [HI 👋👋👋] (https://twitter.com/thecoollearner)

Happy Coding 💻