Bind for React Classes

Click Handlers

First, let's see what a click handler looks like in react.

<button onClick={this.handleClick}>click me!</button>

Then we can write the handler in the class:

// our click method
handleClick(){
  console.log( "yay" );
}

This works just like normal js.

But what happens if we start doing stuff- for example we want to access things we did in normal javascript:

this refered to the button being clicked on.

If we console.log it, we can see that it's undefined. This also means we have no way of accessing the other attributes within the class.

Under the hood, what happens with any click handler (not just those in react) is that the window grabs the clikc handler for itself. That is why this in a click handler is the element being clicked on.

How do we set the context / value of this to the current class?

Javascript Bind

this keyword: (review)

In javascript we use this keyowrd to refer to the current context of the function.

Given this ES5 JS:

var saySomething = {
  message : "hello",
  speak : function(){
    alert( this.message );
  }
};

saySomething.speak();

When we look at this.message, it refers to the message key inside the current object.

Given this code:

<button id="submit">submit</button>
document.querySelector('#submit').addEventListener('click',function(){
  console.log(this);
});

this keyword refers to the button being clicked, or, the current context of execution.

JS Execution Context

What would happen if we used our speak method as a click callback?

document.querySelector('#submit').addEventListener('click',saySomething.speak);

What is actually happening is that when you set a callback is that the DOM holds onto your callback function- the context of the execution changes.

Why we need bind:

With bind javascript gives us an explicit way to set the function context before the function gets executed.

saySomething.speak = saySomething.speak.bind( saySomething );

From MDN:

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

When we say:

<button onClick={this.handleClick}>click me!</button>

React isn't doing any magic to fix the above problem.

We need to bind our handleClick method to the current component.

Exercise: JS bind

Start a new index.html file:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  </head>
  <body>
  <script>
  </script>
  </body>
</html>

In the script tag:

var saySomething = {
  message : "hello",
  speak : function(){
    alert( this.message );
  }
};

saySomething.speak();

Call saySomething.speak() from the dev console to make sure it works.

Add a button into the body html

<button id="submit">submit</button>

Set a click handler to the saySomething method.

document.querySelector('#submit').addEventListener('click',saySomething.speak);

The message will be undefined.

See the return value of bind

saySomething.speak.bind( saySomething );

Fix it by doing a bind before you set the click handler.

saySomething.speak = saySomething.speak.bind( saySomething );

Further

Does this work instead? Why?

document.querySelector('#submit').addEventListener('click',saySomething.speak.bind(saySomething));

Bind in react:

Run this code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Bind</title>
</head>
<body>

  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

  <div id="root"></div>
  <script type="text/babel">

    class List extends React.Component {

        constructor(){
          super();
          this.yay = "banana";
        }

        // our click method
        handleClick(){
          console.log( "yay" );
          console.log( this.yay );
        }

        render() {
            return (
              <div>
                <button onClick={this.handleClick}>click me!</button>
                <ul>
                  <li>Hello world</li>
                </ul>
              </div>
            );
        }
    }

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

  </script>

</body>
</html>

Add bind in the consturctor to make it work:

this.handleClick = this.handleClick.bind(this);

results matching ""

    No results matching ""