Pragmatic Pineapple 🍍

Losing .bind(this) in React

Published Last updated loading views

Getting rid of .bind(this) in React component.

Dark cane

Photo by JANNIK SELZ on Unsplash

bind this will be past

This will be the past

If you used React in your time, you probably had to write some of .bind(this) code. And yes, I know:

  • it looks pretty ugly, plus,
  • it’s taking up some extra space in the codebase.

Luckily, there are some proposed features of JavaScript that can make .bind(this) the past for us.

Before I explain how to lose .bind(this), I’ll show you a short example of where this can be used. Let’s say we want to render a button which changes its text when you click it. In order to do that, we would write a component similar to the one below 🔽.

import React, { Component } from "react"

class ButtonWithBind extends Component {
  constructor() {
    super()

    this.state = { toggle: false }
  }

  toggleButton() {
    this.setState((prevState) => ({ toggle: !prevState.toggle }))
  }

  render() {
    const toggle = this.state.toggle

    return (
      <div>
        <button onClick={this.toggleButton}>{toggle ? "ON" : "OFF"}</button>
      </div>
    )
  }
}

export default ButtonWithBind

We set the toggle switch in the state to false in our constructor.

Also, we add the toggleButton function as onClick handler function, so it will get called when the button is clicked.

And, we create a simple toggleButton function which toggles the state when called.

Awesome, seems like we’re good to go!

If we go ahead an click the rendered button, we’ll get a TypeError like this:

Can't set state

Dang it! It should work 🤔.

We’re getting an error because this is not defined when onClick calls our toggleButton function.

Usually, you would fix this by binding this to the toggleButton function so it always stays the same. Let’s go ahead and bind this to our function in the constructor with:

this.toggleButton = this.toggleButton.bind(this)

After adding it, our button component should look like this:

import React, { Component } from "react"

class ButtonWithBind extends Component {
  constructor() {
    super()

    this.state = { toggle: false }

    this.toggleButton = this.toggleButton.bind(this)
  }

  toggleButton() {
    this.setState((prevState) => ({ toggle: !prevState.toggle }))
  }

  render() {
    const toggle = this.state.toggle

    return (
      <div>
        <button onClick={this.toggleButton}>{toggle ? "ON" : "OFF"}</button>
      </div>
    )
  }
}

export default ButtonWithBind

Try it out, it should do it’s work:

It works!

Yay, it’s working! 🍾

🔪 .bind(this)

Now, let’s get rid of that annoying .bind(this). In order to do that, we’ll use experimental public class field feature in JavaScript. Public class field feature allows you to use arrow function syntax in your classes:

toggleButton = () => {
  this.setState((prevState) => ({ toggle: !prevState.toggle }))
}

An arrow function does not have its own this, but it has the this value of the enclosing execution context. Arrow Functions lexically bind their context so this actually refers to the originating context. That’s called Lexical Scoping if you’re into naming things. Basically, it saves us from doing .bind(this) in our code.

Note that this is an experimental feature in JS, which means it’s not yet accepted into ECMAScript standard, but let’s keep our fingers crossed that it will 🤞. Until that happens, you can configure babel to transpile it using babel-plugin-transform-class-properties.

Also, if you’re using create-react-app by any chance, public class fields are supported out of the box, so no additional setup is needed 🤘

Possible pitfalls

Keep in mind that this can affect two things. First thing is memory and performance. When you use a class field to define a function, your method resides on each instance of the class and NOT on the prototype as it does using the bind method. You can read about this in depth in a great article by Donavon West - “Demystifying Memory Usage using ES6 React Classes“.

Second thing that can be affected by using public class field is how you write your unit tests. You won’t be able to use component prototype to stub on function calls like this:

const spy = jest.spyOn(ButtonWithoutBind.prototype, "toggleButton")
expect(spy).toHaveBeenCalled()

You will have to find another way to stub the method, either by passing the spy in props or checking the state changes.

Using it inside the component

Now, let’s jump right in how we can use public class field in our component and change our toggleButton function in order to lose .bind(this):

import React, { Component } from "react"

class ButtonWithoutBind extends Component {
  constructor() {
    super()

    this.state = { toggle: false }
  }

  toggleButton = () => {
    this.setState((prevState) => ({ toggle: !prevState.toggle }))
  }

  render() {
    const toggle = this.state.toggle

    return (
      <div>
        <button onClick={this.toggleButton}>{toggle ? "ON" : "OFF"}</button>
      </div>
    )
  }
}

export default ButtonWithoutBind

Every React developer ever: looks at line 22–24 “WOW, so pretty 💅. No more of that pesky little .bind(this).”

What’s also great about public class fields is that we can define state right out of the constructor, and slim down our component:

import React, { Component } from "react"

class ButtonWithoutBind extends Component {
  state = { toggle: false }

  toggleButton = () => {
    this.setState((prevState) => ({ toggle: !prevState.toggle }))
  }

  render() {
    const toggle = this.state.toggle

    return (
      <div>
        <button onClick={this.toggleButton}>{toggle ? "ON" : "OFF"}</button>
      </div>
    )
  }
}

export default ButtonWithoutBind

And voilà, we’ve lost .bind(this), and we’ve slimmed down our component a bit, I call this a victory 🏁! We deserve some kind of an award. Feel free to stroll down the fridge and grab yourself a cold one 🍺, or a chocolate 🍫, or whatever you fancy, cus you just learned a whole new thing you can do in React 🎉.

Big thanks to Kent C. Dodds for making a video about this. This article wouldn’t exist without him. Cheers Kent 🍻.

If you liked what you saw, please 👏 and spread the word. Also, check out my website and follow me. I’ll be posting more of React related articles, so click “Follow” and stay tuned 🎥.

Also, retweeting this is a great way to help spread the word with your friends:

Good luck! 🍻


This article was originally posted on Medium


Nikola Đuza

Written by Nikola Đuza who helps developers improve their productivity by sharing pragmatic advice & applicable knowledge on JavaScript and Ruby. You can connect with him on Twitter.

© 2024 Nikola Đuza