1

I have the following component in react

import React, { Component } from 'react';
import axios from 'axios'
import Post from './Post'
import Navbar from '../Navbar'

class SinglePost extends Component {
    constructor(props) {
        super(props);
        this.state = { 
            post: {}
        }
    }
    componentDidMount() {
        const { post_id } = this.props.match.params
        axios.get(`http://127.0.0.1:8000/posts/${post_id}`)
        .then(res => {
            this.setState({ post: res.data })
            console.log(res.data)
            console.log(this.state)
        })
        .catch(err => console.log(err))
    }
    render() { 
        return (
            <div>
                <Navbar />
                <div style={{height: '95px'}}></div>
                <Post key={this.state.post.id} post={this.state.post} />
            </div>
        )
    }
}

export default SinglePost;

The correct response is recieved from the backend and set to state properly. However the data passed to the Post component shows up as undefined.

Here is teh post component:

import React from 'react'
import Comment from './Comment'
import CommentForm from './CommentForm'

const placeholder_url = 'https://image.shutterstock.com/image-vector/ui-image-placeholder-wireframes-apps-260nw-1037719204.jpg'
const placeholder = 'data:image/png;base64,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'

function Post(props) {
    console.log(props) //Shows up as an empty object
    return (
        <div className="uk-background-muted uk-margin-top uk-container">
            <div>
                <div className="uk-background-default"> 
                    <img src={props.post.user.pp || placeholder} width="50" height="50" alt={props.post.user.username} />
                    <a href={`/users/${props.post.user.id}`}>{props.post.user.username}</a>
                </div>
                <img className="uk-align-center" src={props.post.image || placeholder_url} alt="placeholder" />
                <ul>
                    <li>Likes: {props.post.post_liked.length}</li>
                    <li>Dislikes: {props.post.post_disliked.length}</li>
                    <li>Comments: {props.post.comment_post.length}</li>
                </ul>
                <p className="uk-text-large">{props.post.text}</p>
                {props.post.comment_post.map(comment => (<Comment key={comment.id} comment={comment} />))}
                <div>
                <CommentForm key={props.post.id} post={props.post} />
                </div>
            </div>
        </div>
    )
  }

export default Post

This is the error I get: TypeError: Cannot read property 'pp' of undefined

svoruganti
  • 307
  • 1
  • 12
  • 4
    This is because on first render post is {} (an empty object) , in SinglePost before the return just do a checking if(this.state.post === {}) return null, to avoid rendering Post with an empty object. Or check it in the Post component right before the return : if(props.post === {}) return null – CevaComic Jun 09 '20 at 16:37
  • @CevaComic's answer won't work, because you can't compare empty objects in Javascript... `{} === {}` = `false`. See https://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object – simmer Jun 09 '20 at 16:44
  • 1
    Sorry, my bad, I usually make the check like so if(!post.id) , I never used that method but i thought it works. Thanks @simmer – CevaComic Jun 09 '20 at 16:48
  • No worries. It's not intuitive! – simmer Jun 09 '20 at 16:50

2 Answers2

1

State-setting is asynchronous, so whenever you want to act upon data from state, you should include a manual check against undefined values.

Try something like this:

class SinglePost extends Component {
    constructor(props) {
        super(props);
        this.state = { 
            post: null // null initial value is easier to check than an empty object
        }
    }
    componentDidMount() {
        const { post_id } = this.props.match.params
        axios.get(`http://127.0.0.1:8000/posts/${post_id}`)
        .then(res => {
            this.setState({ post: res.data })
            console.log(res.data)
            console.log(this.state)
        })
        .catch(err => console.log(err))
    }
    render() { 
        return (
            <div>
                <Navbar />
                <div style={{height: '95px'}}></div>

                {this.state.post &&
                    <Post key={this.state.post.id} post={this.state.post} />
                }
            </div>
        )
    }
}
simmer
  • 2,275
  • 1
  • 16
  • 20
  • I tried this it still doesnt work, the condition results in true, so the data gets passed to teh post, but it still shwos up as undefinded. – svoruganti Jun 09 '20 at 16:48
  • Ok, I've updated my answer to use `null` as an initial value for `this.state.post`, which will be easier to check against. Try that. – simmer Jun 09 '20 at 16:53
1

The error happens in the first render when post = {} and the Post component is trying to get props.post.user.pp.

To solve the problem, in the SinglePost component, you could initialize post to be null instead of {} and in the Post component, handle the post === null case:

function Post(props) {
  if(!props.post) {
    return null;
    // return <span>Loading</span>;
  }
  return (
      <div className="uk-background-muted uk-margin-top uk-container">
        ...
Hangindev
  • 2,748
  • 5
  • 17