Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - Why is binding this not required in this example?

Trying to figure out the basics of React.

Looking at the second example on this page: https://facebook.github.io/react/ I see that the tick() function sets the state of the Timer class, incrementing the previous value by one.

class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {secondsElapsed: 0};
  }

  tick() {
    this.setState((prevState) => ({
      secondsElapsed: prevState.secondsElapsed + 1
    }));
  }

  componentDidMount() {
    this.interval = setInterval(() => this.tick(), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    return (
      <div>Seconds Elapsed: {this.state.secondsElapsed}</div>
    );
  }
}

ReactDOM.render(<Timer />, mountNode);

However, when I tried to implement my own simple Counter class, it failed and I got a console error saying Cannot read property setState of undefined.

class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.state = {count: 0};
    }

    increment(prevState) {
        this.setState((prevState) => ({
            count: prevState.count + 1
        }));
    }

  render() {
    return (
            <div className="main">
                <button onClick={this.increment}>{this.state.count}</button>
            </div>
    )
  }
}

Some Googling reveals that I have to bind this to the increment function. But why was that not required in the first example that I saw? I copied the code to CodePen and it ran fine with React 15.3.1 I cannot find anything resembling binding in that example. Only after I added binding code in the constructor did things start working in my example.

class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.state = {count: 0};

        // THIS ONE LINE FIXED THE ISSUE
        this.increment = this.increment.bind(this);
    }

    increment(prevState) {
        this.setState((prevState) => ({
      count: prevState.count + 1
    }));
    }

  render() {
    return (
            <div className="main">
                <button onClick={this.increment}>{this.state.count}</button>
            </div>
    )
  }
}
like image 525
Adam Avatar asked Jan 26 '17 05:01

Adam


People also ask

Why we dont need to bind this in arrow function?

Why? Because, arrow functions do not bind their own this, instead, they inherit the one from the parent scope or lexical scope. So, it inherited the value of this from App component and that's why there is not need to explicitly bind it.

Why do you have to bind this in React?

Binding methods helps ensure that the second snippet works the same way as the first one. With React, typically you only need to bind the methods you pass to other components. For example, <button onClick={this.handleClick}> passes this.handleClick so you want to bind it.

How do you avoid need for binding in React?

To avoid the need for binding we have something introduced in ES6 as arrow functions. Using the arrow function to call this. setState will lead to avoid the use of bind.

Why do you need to bind this?

bind(something) returns a new function, in which references to this will refer to something . This is a way of saving the current value of this , which is in scope during the call to the constructor, so that it can be used later when the function is called.


2 Answers

Answering your question: the first example uses arrow functions, that automatically performs context binding. From the docs:

An arrow function does not create its own this context, so this has its original meaning from the enclosing context.

Indeed there are some ways of binding in React:

1) you can bind all functions in your constructor, like you said:

constructor(props) {
    /* ... */
    this.increment = this.increment.bind(this);
}

2) invoke your callbacks with arrow functions:

<button onClick={(e) => this.increment(e)}>

3) append .bind at the end of your method reference each time you set it as a callback, like this:

<button onClick={this.increment.bind(this)}>

4) In your class, define the method with arrow functions:

increment = (e) => {
  /* your class function defined as ES6 arrow function */
}

/* ... */

<button onClick={this.increment}>

In order to use this syntax with babel, you have to enable this plugin or use stage-2 preset.

like image 101
mrlew Avatar answered Oct 02 '22 12:10

mrlew


If you look closely at the way the tick() function has been called in your fist example, you will understand that binding has been specified to it when it is called using the arrow functions. If you do the same for the increment function it will also work. These are just different ways of binding the functions.

So as asked, its not that in the first example no binding is specified while the second it requires, rather in both your cases you are binding just that the way of binding is different for both the cases.

class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.state = {count: 0};

        
       
    }

    increment(prevState) {
        this.setState((prevState) => ({
      count: prevState.count + 1
    }));
    }

  render() {
    return (
            <div className="main">
                <button onClick={() => this.increment()}>{this.state.count}</button>
            </div>
    )
  }
}

ReactDOM.render(<Counter/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
like image 37
Shubham Khatri Avatar answered Oct 02 '22 13:10

Shubham Khatri