Avi
Avi

Reputation: 686

React event without binding this

I'm currently in the process of learning React and I've come across something that seems weird in React's Getting Started guides.

I'm currently reading this section. There's this code sample: https://codepen.io/gaearon/pen/QKzAgB?editors=0011

It showcases conditional rendering, that's not the point of my question though. When they pass the HandleLogout/LoginEvent, they just pass this.HandleLoginEvent, without binding or using arrow functions, yet this code works perfectly, how does it work?

The piece of code I'm talking about is this:

let button = null;
    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />;
    }

In the previous section of the guides they explicitly state you have to use some method to bind the "this" in order for "this" not to be undefined when called from a child component, which makes sense.

Yet here "this" is somehow magically bound, how is it done?

Thanks, Avi.

EDIT: As Ori kindly pointed out, there's a bind call I've missed, problem solved :)

Upvotes: 0

Views: 1336

Answers (2)

Liren Yeo
Liren Yeo

Reputation: 3451

There are multiple ways to handle React binding pattern:

Bind in render

render() {
  return (
    <LogoutButton onClick={::this.handleLogoutClick} />
    {/* or */}
    <LogoutButton onClick={this.handleLogoutClick.bind(this)} />
  )
}

Bind in constructor

As shown in the codepen, which explains why you don't see binding in render.

constructor(props) {
  super(props)
  this.handleLoginClick = this.handleLoginClick.bind(this)
  // or
  this.handleLoginClick = ::this.handleLoginClick
}

Use arrow function

When you use arrow function to declare handleLogoutClick, the function uses lexical binding.

Normally in JS, the value of this is determined by how a function is called. But with ES6 arrow function, we are able to create function that behaves differently - it retains the this value of the enclosing lexical context, now we don't even have to call bind!

handleLogoutClick = () => {
  this.setState({isLoggedIn: false});
}
// and you can simply
onClick={this.handleLogoutClick}

Personally I definitely prefer arrow function, as it produces cleaner code, and I don't have to write that constructor just to bind stuffs. I can simply do:

class LoginControl extends React.Component {
  state = {isLoggedIn: false}

  //... other stuffs ...
}

As for binding in render (or arrow function inside render), you should always avoid that.

When working with PureComponent, binding in render will cause unnecessary re-rendering.

Why Arrow Functions and bind in React’s Render are Problematic

Upvotes: 4

ThatBrianDude
ThatBrianDude

Reputation: 3190

  constructor(props) {
    super(props);
    this.handleLoginClick = this.handleLoginClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
    this.state = {isLoggedIn: false};
  }

When you do it this way you avoid forgetting to bind them when passing them all over the place.

Upvotes: 1

Related Questions