1

Reading through React docs I came across the following piece of code:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

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

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

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

My question is this:

In componentDidMount() the arrow function () => this.tick() "captures" the enclosing this value and thus the proper tick method is called on the <Clock> component.

However tick uses this on its own to subsequently call the setState method. How is this working without any bindings? In other words, how is this.setState working?

Thanks

6
  • ()=> actually binds current this to function. Its similar to function(){}.bind(this). You can also try setTimeout(this.tick.bind(this), 1000) Commented Apr 4, 2017 at 5:48
  • @Rajesh What you are suggesting only applies to this.tick. Inside the tick method however this is used again to call setState. That's the this that troubles me the most. Commented Apr 4, 2017 at 6:01
  • What do you think would this hold in tick? Commented Apr 4, 2017 at 6:02
  • @Rajesh I believe that this within the tick function should be undefined or the global Object/window. Not a react component though... Commented Apr 4, 2017 at 6:07
  • 1
    For clarity on this, refer: stackoverflow.com/questions/3127429/…. A simple explanation, a function's this points to callee(instance associated with it) or default(window or undefined). Now when you do ()=>, you are binding this of componentDidMount to tick, which points to `component. Hence setState works fine Commented Apr 4, 2017 at 6:20

2 Answers 2

1

According to the bind function documentation, Function.prototype.bind will create a new function with the same body and scope as the original function, but where this in the scope of the new function refers to the argument that is passed into bind. This means you can pass a variable, lets say newContext into the bind function like: tick.bind(newContext), and every instance of this inside of tick will be evaluated as newContext. Although this is a keyword referring to the context of the scope, we have changed the scope's context and so this has a different value.

The arrow notation does something similar, and will replace the this inside the value tick with the value of the context of the arrow function's scope. This is why the this keyword inside of tick does not have an undefined value.

Sign up to request clarification or add additional context in comments.

Comments

1

I think Rajesh has already explained it in the comments, still I will give it a try to explain.

An arrow function does not create its own this context, so this has its original meaning from the enclosing context i.e from where it is being called which is componentDidMount in your case.

  componentDidMount() {
    // this refers to React.Component instance
    this.timerID = setInterval(
      () => this.tick(),  // <----------------Same this is available here
      1000
    );
  }

If you use bind you can achieve similar things via setting context via bind.

  componentDidMount() {
    // this refers to React.Component instance from inside cdm method
    this.timerID = setInterval(
      this.tick.bind(this),  // <----------------Same this is available here
     // We have set it react component instance via bind
      1000
    );
  }

Now after all those just look only at this method declaration -

  tick() {
    this.setState({
      date: new Date()
    });
  }

Upto this point we can not say exactly what will be the value of this inside tick. We also can not say that 'setState' will be available on this or not.

Unless and until we see from where tick will get called and what execution context will be attached to tick at run time.

Now if we see from where tick is getting called - componentDidMount. componentDidMount is react component's life cycle method and it ( associated context i.e this) definitely has setState method available.

And in componentDidMount we had set this to react-component context either via arrow function () => {} or via bind. This is the reason because of which setState is avaialble in tick when called and you don't see any error.

But if you don't use arrow function or explicit bind you will be receiving following error on each time when setInterval calls tick function there by associating a global this with tick.

  "message": "Uncaught TypeError: this.setState is not a function",

See the below code snippet via running to see the error

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    // now this inside tick will be pointing to setIntervals execution context which could be global
    this.timerID = setInterval(
      this.tick,
      1000
    );
  }

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

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);
<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="root">
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.