0

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 :)

Avi Sasson
  • 605
  • 6
  • 20

2 Answers2

4

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

Liren Yeo
  • 2,372
  • 2
  • 12
  • 33
  • What is the :: syntax? I know in Angular it means 1 time binding, what does it mean in react? What does it actually do? – Avi Sasson Dec 21 '17 at 13:04
  • 1
    @AviSasson: `::` has nothing to do with react. It's a stage 0 proposal for ECMAScript: https://github.com/tc39/proposal-bind-operator – Felix Kling Dec 21 '17 at 16:32
  • 1
    @AviSasson This answer is short and easy to understand: https://stackoverflow.com/questions/31220078/javascript-double-colon-bind-operator – Liren Yeo Dec 22 '17 at 08:57
1
  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.

ThatBrianDude
  • 2,312
  • 2
  • 10
  • 40