4

I'm following the React Beginner Tutorial and I'm trying to translate it into ES6. However when I changed the CommentBox to an ES6 class it started giving me a this.props.url is undefined error (in the AJAX call in loadCommentsFromServer). I think this has something to do with how ES6 binds this, but this I'm not very familiar with the language (nor React) so I'm not sure. I've looked at the React 0.13 release notes and saw this:

React.createClass has a built-in magic feature that bound all methods to this automatically for you. This can be a little confusing for JavaScript developers that are not used to this feature in other classes, or it can be confusing when they move from React to other classes.

I'm not exactly sure but I thought that it meant I had to save the value of this (as in let that = this and .bind(that)) but that also gave the same this.props.url is undefined - I'm not sure where to go next.

Here's my current code:

class CommentBox extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: []
    };
  }
  loadCommentsFromServer() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({
          data: data
        })
      }.bind(this)
    });
  }
  handleCommentSubmit(comment) {
    var comments = this.state.data;
    var newComments = comments.concat([comment]);
    this.setState({ data: newComments });
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'POST',
      data: comment,
      success: function(data) {
        this.setState({ data: data });
      },
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  }
  componentDidMount() {
    this.loadCommentsFromServer();
    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
  }
  render() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data}/>
        <CommentForm onCommentSubmit={this.handleCommentSubmit}/>
      </div>
    );
  }
};
0

3 Answers 3

5

You need use bind(this) to bind your events. like below:

componentDidMount() {
    this.loadCommentsFromServer().bind(this);
    setInterval(this.loadCommentsFromServer.bind(this), this.props.pollInterval);
  }

You could read the reference from this link: https://facebook.github.io/react/docs/reusable-components.html#no-autobinding

No Autobinding Methods follow the same semantics as regular ES6 classes, meaning that they don't automatically bind this to the instance. You'll have to explicitly use .bind(this) or arrow functions =>.

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

3 Comments

and since I can't use arrow functions in this instance, I guess bind(this) is the only solution?
I mad a mistake before that "this.loadCommentsFromServer()" cannot bind(this).
Btw, if you want to use arrow functions .the only way is like this:setInterval(()=>{ $.ajax({ url: this.props.url, dataType: 'json', cache: false, success: function(data) { this.setState({ data: data }) }.bind(this) });},this.props.pollInterval);
1

Within callback functions like success and error, the scope changes, so "this" is no longer the CommentBox.

You need to do something like:

handleCommentSubmit(comment) {
    var comments = this.state.data;
    var newComments = comments.concat([comment]);
    this.setState({ data: newComments });
    var comment_box = this;
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'POST',
      data: comment,
      success: function(data) {
        comment_box.setState({ data: data });
      },
      error: function(xhr, status, err) {
        console.error(comment_box.props.url, status, err.toString());
      }.bind(this)
    });
  }

Apply this to other applicable places in your code

4 Comments

It was working when it was written with ES5 and React.createClass, did anything in ES6 change in the way that this is bound to callback functions?
I am not sure. I would recommend also adding console.log(this) within the callback functions so you can verify what "this" is.
Ok, so this works once (it shows the right object) and then it starts giving me the undefined error
Sorry can you be more specific about which part works once? And which part is giving you the undefined error again? is it loadCommentsFromServer() still? Let me know after you put console.log(this) within all callbacks. Did you apply my method above to loadCommentsFromServer() ?
1

This is a behavior specific to the implementation of React.Component as an ES6 class. When used ES5 style, React components autobind all of their functions. When you use the ES6 class style, the only methods autobound are those specifically included in React.Component (render, componentDidMount, etc).

This is actually mentioned in the documentation, though it's easy to overlook.

And don't feel bad; I know it's in the documentation because I had to go looking for it the first time I ported some working React components into ES6 classes.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.