Mike Mike - 1 month ago 12
React JSX Question

Uncaught (in promise) TypeError: Cannot read property 'images' of undefined(…)

I am learning React and am not sure how to solve my issue. I am importing data from Reddit. I am able to output the titles and id from the JSON data, but when I attempt to do the same with the images url I get Uncaught (in promise) TypeError: Cannot read property 'images' of undefined(…).

Here is my code (the variable filteredPosts is where the error occurs) and below I will include a portion of the JSON file:

import React from "react";
import Projects from "./Projects";
import axios from 'axios';

export default class MainContainer extends React.Component{
constructor(props){
super(props);

this.state = {
posts: []
}

}

componentDidMount(){
axios.get('https://www.reddit.com/r/reactjs.json')
.then(res => {
const posts = res.data.data.children.map(obj => obj.data);
this.setState({posts});
})

}

render() {

var filteredPosts = this.state.posts.map(post => <Projects key={post.id} title={post.title} imgSrc={post.preview.images[0].source.url}></Projects>);

return (
<div>
<div className="container">
<div className="row">
<div className="col-lg-12">
<h1 className="page-header">My Project
<small> made with React.js</small>
</h1>
</div>
</div>
<div className="row">
{filteredPosts}
</div>
<hr/>
<div className="row text-center">
<div className="col-lg-12">
<ul className="pagination">
<li>
<a href="#">&laquo;</a>
</li>
<li className="active">
<a href="#">1</a>
</li>
<li>
<a href="#">2</a>
</li>
<li>
<a href="#">3</a>
</li>
<li>
<a href="#">4</a>
</li>
<li>
<a href="#">5</a>
</li>
<li>
<a href="#">&raquo;</a>
</li>
</ul>
</div>
</div>
</div>
</div>
);
}
}


and a portion of the JSON data

{
"kind": "Listing",
"data": {
"modhash": "",
"children": [
{
"kind": "t3",
"data": {
"contest_mode": false,
"banned_by": null,
"domain": "appendto.com",
"subreddit": "reactjs",
"selftext_html": null,
"selftext": "",
"likes": null,
"suggested_sort": null,
"user_reports": [

],
"secure_media": null,
"saved": false,
"id": "5b4ly7",
"gilded": 0,
"secure_media_embed": {

},
"clicked": false,
"report_reasons": null,
"author": "kylebythemile",
"media": null,
"name": "t3_5b4ly7",
"score": 12,
"approved_by": null,
"over_18": false,
"removal_reason": null,
"hidden": false,
"preview": {
"images": [
{
"source": {
"url": "https:\/\/i.redditmedia.com\/SQmtP8sZ_KVQ7ro86Nxtqbm7pv_9vGRYQasz4WHkcno.jpg?s=666b08d702fd9102b613696796eb024a",
"width": 300,
"height": 140
},
"resolutions": [
{
"url": "https:\/\/i.redditmedia.com\/SQmtP8sZ_KVQ7ro86Nxtqbm7pv_9vGRYQasz4WHkcno.jpg?fit=crop&amp;crop=faces%2Centropy&amp;arh=2&amp;w=108&amp;s=8ad5dbf3e11f262813db5ae0abde322b",
"width": 108,
"height": 50
},
{
"url": "https:\/\/i.redditmedia.com\/SQmtP8sZ_KVQ7ro86Nxtqbm7pv_9vGRYQasz4WHkcno.jpg?fit=crop&amp;crop=faces%2Centropy&amp;arh=2&amp;w=216&amp;s=35c1ca12d95578caf11c525dc8344197",
"width": 216,
"height": 100
}
],
"variants": {

},
"id": "6-DU1uRDjVj1MLweYNbxm9T8aMz-XOV8yO5aoKkR1FI"
}
]
},
"thumbnail": "http:\/\/b.thumbs.redditmedia.com\/mmhbul366OESsJ0BNfSq5rCUsJq74s7R40zzlzwpH6w.jpg",
"subreddit_id": "t5_2zldd",
"edited": false,
"link_flair_css_class": null,
"author_flair_css_class": null,
"downs": 0,
"mod_reports": [

],
"archived": false,
"media_embed": {

},
"post_hint": "link",
"is_self": false,
"hide_score": false,
"spoiler": false,
"permalink": "\/r\/reactjs\/comments\/5b4ly7\/build_a_coffee_finder_app_with_react_native_and\/",
"locked": false,
"stickied": false,
"created": 1478306294,
"url": "https:\/\/appendto.com\/2016\/11\/build-a-coffee-finder-app-with-react-native-and-the-yelp-api\/?reddit",
"author_flair_text": null,
"quarantine": false,
"title": "Build a Coffee Finder App with React Native and the Yelp API",
"created_utc": 1478277494,
"link_flair_text": null,
"distinguished": null,
"num_comments": 0,
"visited": false,
"num_reports": null,
"ups": 12
}
},

Answer

The problem is that you are assuming every post is going to have a preview property. I did the ajax call myself and did a console log on each post and found at least 6 didn't have the preview property.

You need to be more defensive and have a default image or just blank background. you could do something like:

imgSrc={post.preview ? post.preview.images[0].source.url : defaultImg}

that is assuming you have a defaultImg if not just return an empty string in it place.

there is also an assumption there that if the preview property exists that the image property will also exist as well as source.url. Its always good to do an in depth research on the api you are using to see where its consistent and where it's not and where it's not do more defensive programming by checking if property exists before checking for sub-properties.

Also as a final option, if you only wanted post with a preview you could do a simple filter:

this.state.posts.filter((post) => post.preview) // returns only posts with preview property