Home > Software design >  ×React - TypeError: Cannot read properties of undefined (reading 'userName')
×React - TypeError: Cannot read properties of undefined (reading 'userName')

Time:11-01

can anyone please explain me thiss i am getting a json through my server and that json contain the information i am using in the blog so i am able to access blog.blogTitle but i am not able to access blog.owner.userName it makes no sense to me because i am setting whole json at once on the state then using it can please anyone tell me the reason? and how to avoid it

function Blog(props) {
    const isUserAuthenticated = useSelector(state => state.getUser.authenticated)

    const user = useSelector(state => state.getUser.user)

    const [blog, setBlog] = useState([])

    const [dataMounted, setDataMounted] = useState(false)

    const [blogComments, setBlogComments] = useState([])

    const handleCommentSubmission = (e) => {
        const body = {
            blogId: blog.id,
            comment: e.comment,
            userName: user.userName
        }
        console.log(body)

        axios.post('http://localhost:8989/blog/comment', body, APIService.config).then(response => {
            console.log(response.data)
        }).catch(err => {
            console.log("Error while fetching comment: "   err)
            console.log("Error while fetching comment Server response: "   err.response)
        })

        return false;
    }

    const commentSchema = yup.object().shape({
            comment: yup
                .string().required('Comment is required')
                .min(2, 'Minimum 2 characters required')
                .max(300, "Maximum 300 characters allowed"),
        }
    );

    useEffect(() => {

        APIService.getBlogById(props.match.params.id).then(response => {
            setBlog(response.data)
            setBlogComments([])

            console.log('comments:', response.data)
            if (!response.data.comments.includes(null)) {
                response.data.comments.forEach(commentId => {
                    APIService.getCommentById(commentId).then(comment => {
                        blogComments.push(comment.data)
                    })
                })
            }

            console.log("Blog comments  ", blogComments)
        }, [])
        setDataMounted(true)

    }, [])

    return (
        <>
            <div className='blog-container'>

                <Card className="bg-dark text-white blog-banner">
                    <Card.Img
                        className='blog-banner'
                        src="https://image.freepik.com/free-vector/abstract-dotted-banner-background_1035-18160.jpg"
                        alt="Card image"/>
                    <Card.ImgOverlay>
                        <Card.Title className='blog-title'>{blog.blogTitle}</Card.Title>
                    </Card.ImgOverlay>

                </Card>


                <Breadcrumb>
                    <Breadcrumb.Item href="#">Home</Breadcrumb.Item>
                    <Breadcrumb.Item href={"/blog/"   blog.blogCategory}>
                        {blog.blogCategory}
                    </Breadcrumb.Item>
                    <Breadcrumb.Item active>{blog.blogTitle}</Breadcrumb.Item>
                </Breadcrumb>

                {dataMounted ?
                    <div className='blog-data-container'>
                        <p className='blog-data'>{blog.data}</p>
                        <p className='blog-owner'>Created By {blog.owner.userName}</p>
                    </div> : <></>
                }

                {isUserAuthenticated ?
                    <div>
                        <EditButton blog={blog}/>
                        <Button className='btn btn-primary'>
                            Delete Blog
                        </Button>
                    </div> : <></>
                }

                {
                    <div className='comment-form'>

                        {dataMounted && isUserAuthenticated ?
                            <Formik
                                initialValues={{
                                    blogTitle: '',
                                    data: '',
                                    blogCategory: '',
                                    accessStatus: ''
                                }}
                                onSubmit={(e) => handleCommentSubmission(e)}
                                validationSchema={commentSchema}
                            >{({
                                   handleSubmit,
                                   handleChange,
                                   handleBlur,
                                   values,
                                   touched,
                                   isValid,
                                   errors,
                               }) => (

                                <Form>
                                    <Form.Group placeholder='Type your blog here'
                                                controlId="blogForm.CommentTextArea">
                                        <Form.Control
                                            as='textarea'
                                            type="textarea"
                                            placeholder="Type your comment here"
                                            name="comment"
                                            value={values.comment}
                                            onChange={handleChange}
                                            isInvalid={!!errors.comment}
                                        />
                                        <Form.Control.Feedback type="invalid">
                                            {errors.comment}
                                        </Form.Control.Feedback>
                                    </Form.Group>

                                    <Button type='submit'
                                            onClick={(e) => handleSubmit(e)}> Add Comment </Button>
                                </Form>
                            )}
                            </Formik>

                            : <></>
                        }
                    </div>}


                <div className='comment-section-container'>
                    <p>{blogComments}</p>
                    {
                        blogComments.map((comment, index) => {
                            return (
                                <Container>
                                    <Row xs={2} md={4} lg={6}>
                                        <Col>1 of 2</Col>
                                        <Col>2 of 2</Col>
                                    </Row>
                                    <Row xs={1} md={2}>
                                        <Col>1 of 3</Col>
                                        <Col>2 of 3</Col>
                                        <Col>3 of 3</Col>
                                    </Row>
                                    <Row xs="auto">
                                        <Col>1 of 3</Col>
                                        <Col>2 of 3</Col>
                                        <Col>3 of 3</Col>
                                    </Row>
                                </Container>
                            )
                        })
                    }
                </div>
            </div>
        </>
    )
}

Example Json

{id: '617ec3047c63c4643862ae51', blogTitle: 'some blog', data: 'datata', date: 1635698971568, owner: {…}, …}
blogAccessStatus: "PUBLIC"
blogCategory: "TECHNICAL"
blogTitle: "some blog"
comments: Array(1)
0: "617ec91bfdce2653b01b1d66"
length: 1
[[Prototype]]: Array(0)
data: "datata"
date: 1635698971568
id: "617ec3047c63c4643862ae51"
owner:
blogCount: 0
email: "[email protected]"
userName: "bhavishya"
[[Prototype]]: Object
sharedWith: []
views: 0
[[Prototype]]: Object

CodePudding user response:

The setDataMounted(true) instruction should be invoked after setBlog(...).

CodePudding user response:

Your blog rendering condition depends on dataMounted state.

{dataMounted ?
  <div className='blog-data-container'>
  <p className='blog-data'>{blog.data}</p>
  <p className='blog-owner'>Created By {blog.owner.userName}</p>
</div> : <></>
}

The problem is you set dataMounted to true outside the callback. That means that it gets executed before the callback to set the state for your blog data runs. A probable fix for your problem is

APIService.getBlogById(props.match.params.id).then(response => {
   setBlog(response.data)
   setBlogComments([])

   console.log('comments:', response.data)
   if (!response.data.comments.includes(null)) {
       response.data.comments.forEach(commentId => {
           APIService.getCommentById(commentId).then(comment => {
              blogComments.push(comment.data)
           })
       })
   }

   console.log("Blog comments  ", blogComments)
   setDataMounted(true) // setDataMounted should be called inside the callback
}, [])
  • Related