how to index into a parsed json array

- 1 answer

Ad

I was going the the reactjs tutorial here, and after completing the tutorial I started tinkering a little bit. One problem I ran into was that I couldn't figure out how to index into a certain array (at least it seems to be an array). Here is the code for when the array is gotten from the server:

componentDidMount: function() {
    $.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 is part of a Reactjs class definition for a class called CommentBox, the json file being parsed looks something like

[
    {id: 1, author: "Pete Hunt", text: "This is one comment"},
    {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
]

So now I tried to change the same CommentBox class's render method to add a line to print the first element of the data array. The render method became

render: function() {
        return (
          < div className = "commentBox" >
            {this.state.data[0].toString()}
            < h1 > Comments < /h1>
            < CommentList data = {this.state.data} / >
            < CommentForm onCommentSubmit = {this.handleCommentSubmit} / >
          < /div>
        );
}

where the line I added was {this.state.data[0].toString()}. This line causes an error which says that this.state.data[0] is undefined. How am I supposed to get at the elements of this.state.data? Shouldn't it just be a normal array, as if it was set by the following code?

this.state.data = [
    {id: 1, author: "Pete Hunt", text: "This is one comment"},
    {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
];
Ad

Answer

Ad

Remember that the "A" in "Ajax" stands for asynchronous; render is called before your data ever makes it back from the server. That means that the first time render is called, this.state.data could be undefined (depending on your getInitialState function).

A common approach is to add a sentinel value that keeps the component from rendering fully — or renders something different — until the data is available:

  getInitialState: function() {
    return {
      loaded: false // `loaded` will be our sentinel value
    };
  },

  componentDidMount: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({
          data: data,
          loaded: true // make sure to set `loaded` to true
        });
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },

  render: function() {
        // if the sentinel isn't set, we're not ready
        if (!this.state.loaded) return <div>Loading...</div>;

        return (
          < div className = "commentBox" >
            {this.state.data[0].toString()}
            < h1 > Comments < /h1>
            < CommentList data = {this.state.data} / >
            < CommentForm onCommentSubmit = {this.handleCommentSubmit} / >
          < /div>
        );
  }

Alternatively, you could check for the value of this.state.data (or something else appropriate) to determine if the data is ready to be rendered or not.

Ad
source: stackoverflow.com
Ad