admin管理员组

文章数量:1306951

I am making an app that gets an array of news items from a remote source and displays them on a page.

I have the endpoint, and can make a successful call, proven by console logs, using $.getJSON(). I placed this call into the parent ponent, because child ponents will need to use the data.

However, when I pass this data down to a child ponent, the console error appears:

Uncaught TypeError: Cannot read property 'headline' of undefined

This is because React is trying to render the ponent even before the data has been passed into it. This makes me think I should first be calling my ajax somewhere other than ponentDidMount.

To get around it, I set up a method on the child ponent that returns the headline if the prop is present:

getHeadline: function () {
    if(this.props.newsItems){
        return this.props.newsItems.headline
    } else {
        return null
    }
},

This feels like a bit of a nasty way around it. Is there a better way, or am I missing something in my code?

var BigStory = React.createClass({

    getHeadline: function () {
        if(this.props.newsItems){
            return this.props.newsItems.headline
        } else {
            return null
        }
    },

    render: function () {
        console.log('props:', this.props);
        console.log('newsItems:', this.props.newsItems);
        return (
            <div className="big-story col-xs-12">
                <div className="col-sm-5">
                    <h1>{this.getHeadline()}</h1>
                    <p>Placeholder text here for now.</p>
                    <p>time | link</p>
                </div>
                <div className="col-sm-7">
                    <img src="" alt=""/>
                </div>
            </div>
        );
    }
});

var Main = React.createClass({

    getInitialState: function () {
        return {
            newsItems: []
        }
    },

    ponentDidMount: function () {
        this.getNewsItems();
    },

    getNewsItems: function () {
        $.getJSON('', (data) => {
            console.log('data sample:', data[0]);
            this.setState({newsItems: data})
        })
    },

    render: function () {
        return (
            <div className="container">
                <div className="main-content col-sm-12">
                    <div className="left-sided-lg-top-otherwise col-lg-8 col-md-12 col-sm-12 col-xs-12">
                        <BigStory newsItems={this.state.newsItems[0]}/>
                    </div>
                </div>
            </div>
        );
    }
});

I am making an app that gets an array of news items from a remote source and displays them on a page.

I have the endpoint, and can make a successful call, proven by console logs, using $.getJSON(). I placed this call into the parent ponent, because child ponents will need to use the data.

However, when I pass this data down to a child ponent, the console error appears:

Uncaught TypeError: Cannot read property 'headline' of undefined

This is because React is trying to render the ponent even before the data has been passed into it. This makes me think I should first be calling my ajax somewhere other than ponentDidMount.

To get around it, I set up a method on the child ponent that returns the headline if the prop is present:

getHeadline: function () {
    if(this.props.newsItems){
        return this.props.newsItems.headline
    } else {
        return null
    }
},

This feels like a bit of a nasty way around it. Is there a better way, or am I missing something in my code?

var BigStory = React.createClass({

    getHeadline: function () {
        if(this.props.newsItems){
            return this.props.newsItems.headline
        } else {
            return null
        }
    },

    render: function () {
        console.log('props:', this.props);
        console.log('newsItems:', this.props.newsItems);
        return (
            <div className="big-story col-xs-12">
                <div className="col-sm-5">
                    <h1>{this.getHeadline()}</h1>
                    <p>Placeholder text here for now.</p>
                    <p>time | link</p>
                </div>
                <div className="col-sm-7">
                    <img src="http://placehold.it/320x220" alt=""/>
                </div>
            </div>
        );
    }
});

var Main = React.createClass({

    getInitialState: function () {
        return {
            newsItems: []
        }
    },

    ponentDidMount: function () {
        this.getNewsItems();
    },

    getNewsItems: function () {
        $.getJSON('http://www.freecodecamp./news/hot', (data) => {
            console.log('data sample:', data[0]);
            this.setState({newsItems: data})
        })
    },

    render: function () {
        return (
            <div className="container">
                <div className="main-content col-sm-12">
                    <div className="left-sided-lg-top-otherwise col-lg-8 col-md-12 col-sm-12 col-xs-12">
                        <BigStory newsItems={this.state.newsItems[0]}/>
                    </div>
                </div>
            </div>
        );
    }
});
Share Improve this question asked Dec 22, 2015 at 19:49 alanbuchananalanbuchanan 4,1738 gold badges43 silver badges66 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 4

I would suggest leaving it up to the parent to decide what to do when it is in a "loading" state and leaving BigStory as a "dumb" ponent that always renders the same assuming it will always receive a valid newsItem.

In this example, I show a <LoadingComponent />, but this could be whatever you need it to be. The concept is that BigStory shouldn't have to worry about edge cases with "receiving invalid data".

var Main = React.createClass({
  // ...
  render() {
    const {newsItems} = this.state;
    // You could do this, pass down `loading` explicitly, or maintain in state
    const loading = newsItems.length === 0;
    return (
      <div className="container">
          <div className="main-content col-sm-12">
              <div className="left-sided-lg-top-otherwise col-lg-8 col-md-12 col-sm-12 col-xs-12">
                  {loading 
                    ? <LoadingComponent />
                    : <BigStory newsItem={newsItems[0]} />  
                  }
              </div>
          </div>
      </div>
    );
  }
});

function BigStory(props) {
  // Render as usual. This will only be used/rendered w/ a valid
  return (
    <div className="big-story col-xs-12">
      <h1>{props.headline}</h1>
      {/* ... */}
    </div>
  )
}

An alternative solution (although I remend an approach more like above) would be to always make use of the BigStory ponent in the same way, but provide it a "placeholder story" when there are no stories loaded.

const placeholderNewsItem = {
  headline: 'Loading...',
  /* ... */
};

var Main = React.createClass({
  // ...
  render() {
    const {newsItems} = this.state;
    // Conditionally pass BigStory a "placeholder" news item (i.e. with headline = 'Loading...')
    const newsItem = newsItems.length === 0
      ? placeholderNewsItem
      : newsItems[0];
    return (
      <div className="container">
          <div className="main-content col-sm-12">
              <div className="left-sided-lg-top-otherwise col-lg-8 col-md-12 col-sm-12 col-xs-12">
                  <BigStory newsItem={newsItem} />
              </div>
          </div>
      </div>
    );
  }
});

This is a pretty mon scenario. John Carpenter's approach would work. Another approach that I use often would be to create some type of Loading ponent with a spinner image (or whatever else you might use to signify that data is on its way). Then, while the client waits for the data to arrive, you can render that Loading ponent. For example:

render: function () {
    if (this.state.newsItems.length === 0) return <Loading />;
    return (
        <div className="container">
            <div className="main-content col-sm-12">
                <div className="left-sided-lg-top-otherwise col-lg-8 col-md-12 col-sm-12 col-xs-12">
                    <BigStory newsItems={this.state.newsItems[0]}/>
                </div>
            </div>
        </div>
    );
}

One way you could do it is to set this.props.newsItems.headline to "loading..." initially to indicate to the user that data will be ing.

本文标签: javascriptPassing asynchronously acquired data to child propsStack Overflow