29

I am trying to do the tutorial: https://facebook.github.io/react/docs/tutorial.html

import React, { PropTypes } from 'react';
import classnames from 'classnames';
import styles from './CommentBox.css';
import withStyles from '../../decorators/withStyles';
import Link from '../../utils/Link';
import $ from 'jquery';

@withStyles(styles)
class CommentBox extends React.Component {

    constructor() {
        super();
        this.state = {data: []};
    }

    loadCommentsFromServer() {
        $.ajax({
            url: this.props.url,
            dataType: 'json',
            cache: false,
            success: function(data) {
                this.setState({data: data});
            }.bind(this),
            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() {

        let url="/public/comments.json"

        return (
            <div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data} />
                <CommentForm />
            </div>
        );
    }

}

class CommentList extends React.Component {

    render() {

        let data = this.props.data

        var commentNodes = data.map(function (comment) {
          return (
            <Comment author={comment.author}>
              {comment.text}
            </Comment>
          );
        });

        return (
          <div className="commentList">
            {commentNodes}
          </div>
        );
    }
};

class Comment extends React.Component {
    render() {
        return(
            <div className="comment">
            <h2 className="commentAuthor">
              {this.props.author}
            </h2>
            {this.props.children}
          </div>
        );
    }
}

class CommentForm extends React.Component {
  render() {
    return (
      <div className="commentForm">
        Hello, world! I am a CommentForm.
      </div>
    );
  }
};

export default CommentBox;

However, the tutorial is a bit outdated and I am using React 0.14-rc1 with ES6 syntax. I have tried my best to follow the tutorial and implementing it the 0.14 way. Was able to get to this point but now getting the error:

TypeError: this.props is undefined

Could not figure out the issue. Any idea why? Thanks

hilarl
  • 6,570
  • 13
  • 48
  • 57

6 Answers6

45

When using React and ES6 classes React won't auto bind functions that is declared on your class.

Therefore either use this.loadCommentsFromServer.bind(this) or use arrow functions

loadCommentsFromServer = () => {}

Henrik Andersson
  • 45,354
  • 16
  • 98
  • 92
  • That seemed to make that error go away. But I still cant seem to load the Json file. I am getting: undefined parsererror SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data app.js:34921:8 loadCommentsFromServer/<.error<() app.js:34921 jQuery.Callbacks/fire() app.js:7771 jQuery.Callbacks/self.fireWith() app.js:7883 done() app.js:12938 .send/callback/<() – hilarl Sep 23 '15 at 08:14
  • 1
    Your json is malformed. – Henrik Andersson Sep 23 '15 at 08:36
  • But I copied and pasted the json data on example on the react tutorial. And it works when I try to load it from inside of the component. Just not working with ajax loading. – hilarl Sep 23 '15 at 08:50
  • Post the json please. – Henrik Andersson Sep 23 '15 at 09:09
  • [ {"author": "Pete Hunt", "text": "This is one comment"}, {"author": "Jordan Walke", "text": "This is *another* comment"}, {"author": "Jordan Walke", "text": "This is *another* comment"} ] – hilarl Sep 23 '15 at 09:14
  • But your JSON is malformed since `this.props.url` is null. – Henrik Andersson Sep 23 '15 at 11:28
  • Is it possible for you to give an example on how to fix this? I am quite new to react. Thanks – hilarl Sep 23 '15 at 11:46
11

With the shift of React from createClass to ES6 classes we need to handle the correct value of this to our methods on our own, as mentioned here: http://www.newmediacampaigns.com/blog/refactoring-react-components-to-es6-classes Change your code to have the method bounded to correct value of this in consructor:

export default class ComponentClass extends React.Component {
  constructor(props) {
      super(props);
      this._myHandler = this._myHandler.bind(this);
  }

  _myHandler(props) {
    console.log(props);
  }

  render() {
    return (
        <div className="col-xs-6 col-md-4">
            <button type="button" className="btn btn-danger" onClick={this._myHandler}><i className="fa fa-trash"> Delete</i></button>
        </div>
    )
  }
}

The no autobinding was a deliberate step from React guys for ES6 classes. Autobinding to correct context was provided with React.createClass. Details of this can be found here: https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding

So based on this you could also change your code as:

export default class ComponentClass extends React.Component {
  _myHandler = (props) => {
    console.log(props);
  }

  render() {
    return (
        <div className="col-xs-6 col-md-4">
            <button type="button" className="btn btn-danger" onClick={this._myHandler}><i className="fa fa-trash"> Delete</i></button>
        </div>
    )
  }
}
Aditya Singh
  • 15,810
  • 15
  • 45
  • 67
8

Passing the props to the constructor will help:

constructor(props) {
    super(props);
}
Rence
  • 2,900
  • 2
  • 23
  • 40
  • This I think is the real issue. If you define a constructor, it needs to accept props as a param while passing it to super. If you don't provide a default construct, React handles this for you. – Jason Hitchings Jan 28 '19 at 06:07
1

Although all of the above answers are technically correct, they did not work in my case. I received some crazy error 'Missing class properties transform' so rather than trying to figure that out I defined the handler right in the event like so:

export class ComponentClass extends React.Component{
  _myHandler(event) {
    event.preventDefault();
    console.log(this.props.thingIPassedIn);
  }
  render() {
    return <Button onClick={(event) => this._myHandler(event)} type="button" className="btn btn-danger">Click Me!</Button>;
  }
}

You can also pass parameters this way.

export class ComponentClass extends React.Component{
  _myHandler(thingIPassedIn) {
    console.log(thingIPassedIn);
  }
  render() {
    return <MyOtherComponent defNotAnEvent={(thingIPassedIn) => this._myHandler(thingIPassedIn)} />;
  }
}
1

The ajax code can be improved like below using arrow function to avoid scope issues and to access props inside

 loadCommentsFromServer = () => {
    $.ajax({
        url: this.props.url,
        dataType: 'json',
        cache: false,
        success: (data) => {
            this.setState({data: data});
        },
        error: (xhr, status, err) => {
            console.error(this.props.url, status, err.toString());
        }
    })
}
Hemadri Dasari
  • 32,666
  • 37
  • 119
  • 162
0

I am also new to React and one thing is worth to notice while working with state and props inside function is USE FUNCTION VARIABLE ie.

func(){this.setState} result error func=()=>{this.setState} will work

same is the case with

this.props inside function

Note even you are not using function.bind(this) with its context inside constructor.