Bind() in javascript

Bind() in javascript

In this post, we’ll dive into how the this keyword behaves when used in event listeners and how to properly bind it in JavaScript, especially with classes. We'll use a simple example to demonstrate the concepts.

Setting the Stage

Let’s start with a basic HTML structure:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React Example</title>
</head>
<body>
    <button id="btn">Button Clicked</button>
</body>
</html>

Now, let's add a JavaScript class that will handle the button click event.

Problem Statement: Why is this Undefined?

Here's the initial JavaScript code that handles the button click event:

class React {
    constructor() {
        this.library = "React";
        this.server = "https://localhost:3000/";

        // Adding the event listener for button click
        document.querySelector("button")
        .addEventListener('click', handleClick);
    }

    handleClick() {
        console.log("button clicked");
        console.log(this.server);  // Expecting "https://localhost:3000/"
        console.log(this.library); // Expecting "React"
    }
}

const app = new React();

When you click the button, you'll encounter the following error in the console:

Uncaught ReferenceError: handleClick is not defined

Reason for this error: The issue in our code is that the handleClick function is being passed as a callback directly to the event listener. When you do this, the value of "this" inside handleClick refers to the element that triggered the event (in this case, the button), not the instance of the React class.

To fix this, you need to bind the correct “this” context (which refers to the React class instance) to the handleClick function.

Adding proper “this“ context

<script>
    class React{
        constructor(){
            this.library = "React";
            this.server = "https://localhost:3000/";

            //requirement
            document.querySelector("button")
            .addEventListener('click', this.handleClick);
        }
        handleClick(){
            console.log("button clicked");
            console.log(this.server);
            console.log(this.library);
        }
    }
    const app = new React();
    app.handleClick();
</script>
//outputs
//button clicked
//undefined
//undefined

Reason for this undefined: The reason you're getting undefined for this.server and this.library even after adding the correct “this” context is because you're still passing the handleClick method directly to the event listener. When this happens, the value of “this” inside handleClick no longer refers to the instance of the React class but instead refers to the element that triggered the event (in this case, the ).

In JavaScript, when a method is used as a callback (as in event listeners), “this” is not preserved, so you need to explicitly bind “this” to the method.

Fixing the Context with bind

To ensure that this inside the handleClick method refers to the React class instance, we need to bind the correct context using bind():

class React {
    constructor() {
        this.library = "React";
        this.server = "https://localhost:3000/";

        // Binding the correct context to the handleClick method
        document.querySelector("button").addEventListener('click', this.handleClick.bind(this));
    }

    handleClick() {
        console.log("button clicked");
        console.log(this.server);  // Outputs: https://localhost:3000/
        console.log(this.library); // Outputs: React
    }
}

const app = new React();

Now, when you click the button, the output will be:

button clicked
https://localhost:3000/
React

Why Do We Need bind()?

When we pass a method (like handleClick) as a callback function to an event listener, the value of this changes. Instead of pointing to the class instance, it points to the element that triggered the event. In this case, this points to the button element (<button>), causing this.server and this.library to be undefined.

By using bind(this), we explicitly tell JavaScript that the this inside handleClick should always refer to the class instance.

Direct Method Calls: What Happens to this?

What if we directly call the method from the class instance without an event listener? Let's see what happens:

class React {
    constructor() {
        this.library = "React";
        this.server = "https://localhost:3000/";
    }

    handleClick() {
        console.log("button clicked");
        console.log(this.server);  // Outputs: https://localhost:3000/
        console.log(this.library); // Outputs: React
    }
}

const app = new React();
app.handleClick();  // Direct call without event listener

In this case, the direct method call works as expected because this refers to the class instance. The output will be:

//button clicked
//https://localhost:3000/
//React

Conclusion

When working with event listeners in JavaScript, especially when using classes, it's crucial to remember that this might not behave as you expect. Without proper context, this will refer to the element triggering the event rather than the class instance. To solve this, always use bind() to explicitly set the value of this.