I'm making an Rails API call and return a single JSON object that has a nested User Object and a tag list array. However, I can't access the nested Object.
this.props.post.user.name throws: Cannot read property 'name' of undefined.
I am confused because when I make the call to PostsIndex in PostsIndex.js and get an array of objects and map through it I can access everything.
Is there something I need to do when only dealing with a single object?
PostShow.js
import React, {Component} from 'react';
import axios from 'axios';
import {Link} from 'react-router-dom';
export default class PostShow extends Component {
constructor(props) {
super(props)
this.state = {
post: {}
};
}
componentDidMount() {
const { match: { params } } = this.props;
axios
.get(`/api/posts/${params.postId}`)
.then(response => {
console.log(response);
this.setState({ post: response.data});
})
.catch(error => console.log(error));
}
render() {
return (
<div>
<Post post={this.state.post}/>
</div>
);
}
}
class Post extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
<div className="centered">
<small className ="small" > | Posted by: {this.props.post.user.name} on | Tags: </small>
<h3>{this.props.post.title}</h3>
<img className="image " src={this.props.post.image}/>
</div>
<div>
<p className = "songTitle"> {this.props.post.song_title} </p>
<p className= "postBody"> {this.props.post.body} </p>
<div className = "link" dangerouslySetInnerHTML={{ __html: this.props.post.link }} />
</div>
</div>
);
}
}
Here is what the JSON object looks like from /api/posts/7:
{"id":7,
"title":"adgaadg",
"body":"adgadgagdgd",
"post_type":"Video",
"tag_list":["ERL"],
"image":"/images/original/missing.png",
"song_title":"adgdgdgd",
"created_at":"2018-08-11T21:57:00.447Z",
"user":{"id":2,"name":"John","bio":"bio","location":"Reno"}}
That's because this.props.post.user
will be undefined
before your request has finished, and trying to access name
on that will give rise to your error.
You could e.g. set the initial post
to null
and not render anything until your request is complete.
Example
class PostShow extends Component {
constructor(props) {
super(props);
this.state = {
post: null
};
}
componentDidMount() {
const {
match: { params }
} = this.props;
axios
.get(`/api/posts/${params.postId}`)
.then(response => {
console.log(response);
this.setState({ post: response.data });
})
.catch(error => console.log(error));
}
render() {
const { post } = this.state;
if (post === null) {
return null;
}
return (
<div>
<Post post={post} />
</div>
);
}
}
axios.get
is an async operation and <Post post={this.state.post}/>
renders before this.setState({ post: response.data});
which means when Post component renders this.state.post
is empty object. So what you can do is, initialize your post with null in constructor
this.state = {
post: null
};
and instead of <Post post={this.state.post}/>
do {this.state.post && <Post post={this.state.post}/>}
it will render post only if its exists and not null.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With