In react/redux when retrieving blog api info from mongodb, only the blog.user.name is undefined all other keys are defined. I get TypeError: Cannot read property 'name' of undefined when trying to insert blog.user.name on the page. Once I remove it from the page, there is no error and all other data is shown on the page. Also when I remove it from the page, I can see in devtools that it is defined, so I'm not sure why I'm getting this error. Randomly when I toggle with adding and removing blog.user.name from the page the name will show up. If it does show up, as soon as I refresh I get the TypeError again.
Also I tried to display just blog.user to see what would happen and I got : Error: Objects are not valid as a React child (found: object with keys {id, name, profilePic}). If you meant to render a collection of children, use an array instead.. Which mean it's getting the data but just not showing it?
BlogScreen - frontend component where I'm trying to display blog.user.name
import React, { useEffect } from 'react'
import moment from 'moment'
import { useSelector, useDispatch } from 'react-redux'
import { Card, Row, Col, Alert } from 'react-bootstrap'
import { getBlogDetail } from '../../actions/blogActions'
import Spinner from '../../components/layout/Spinner'
const BlogScreen = ({ match }) => {
const dispatch = useDispatch()
const blogDetail = useSelector(state => state.blogDetail)
const { loading, error, blog } = blogDetail
useEffect(() => {
if (!blog || blog._id !== match.params.id) {
dispatch(getBlogDetail(match.params.id))
}
}, [match, dispatch, blog])
return (
<>
{loading && <Spinner />}
{error && <Alert variant='danger'>{error}</Alert>}
{blog && (
<>
<div className="blog-main pt-5">
<div className="blog-heading text-center">
<p className="text-muted font-weight-bold">{moment(new Date(blog.createdAt)).format("ddd MMM DD")} | {blog.user.name}</p>
<h3 className="my-5">{blog.title}</h3>
</div>
<p className="content content-one text-justify">{blog.paragraph1}</p>
<div className="blog-image my-4">
<img src={blog.primaryImage} alt="" />
</div>
<p className="content content-two text-justify">{blog.paragraph2}</p>
<div className="blog-image my-4">
<img src={blog.secondaryImage} alt="" />
</div>
<p className="content content-three text-justify">{blog.paragraph3}</p>
</div>
<div className="more-like">
<h3 className="mb-4">More Like This</h3>
<Row>
<Col sm="12" md="4" className="mb-3">
<Card className="h-100">
<Card.Img variant="top" src="/images/connect.jpg" />
<Card.Body>
<Card.Title>Scheduling: Planning Your Day To Be More Productive</Card.Title>
<Card.Text>Wed Jun 4 | Tania Reece</Card.Text>
</Card.Body>
</Card>
</Col>
<Col sm="12" md="4" className="mb-3">
<Card className="h-100">
<Card.Img variant="top" src="/images/connect.jpg" />
<Card.Body>
<Card.Title>Scheduling: Planning Your Day To Be More Productive</Card.Title>
<Card.Text>Wed Jun 4 | Tania Reece</Card.Text>
</Card.Body>
</Card>
</Col>
<Col sm="12" md="4" className="mb-3">
<Card className="h-100">
<Card.Img variant="top" src="/images/connect.jpg" />
<Card.Body>
<Card.Title>Scheduling: Planning Your Day To Be More Productive</Card.Title>
<Card.Text>Wed Jun 4 | Tania Reece</Card.Text>
</Card.Body>
</Card>
</Col>
</Row>
</div>
</>
)}
</>
)
}
export default BlogScreen
Blog Action and Reducer
//action
export const getBlogDetail = (id) => async (dispatch) => {
try {
dispatch({ type: BLOG_DETAIL_REQUEST })
const { data } = await axios.get(`/api/blogs/${id}`)
dispatch({
type: BLOG_DETAIL_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: BLOG_DETAIL_FAIL,
payload: error.response && error.response.data.message
? error.response.data.message
: error.message
})
}
}
//reducer
export const blo
/gDetailReducer = (state = { blog: {} }, action) => {
switch (action.type) {
case BLOG_DETAIL_REQUEST:
return { ...state, loading: true }
case BLOG_DETAIL_SUCCESS:
return {
loading: false,
blog: action.payload
}
case BLOG_DETAIL_FAIL:
return { loading: false, error: action.payload }
default:
return state
}
}
reducer store
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import { blogDetailReducer, blogsFeaturedListReducer, blogsListReducer, blogsRecentListReducer } from './reducers/blogReducers'
// Reducers
const reducer = combineReducers({
blogList: blogsListReducer,
blogFeaturedList: blogsFeaturedListReducer,
blogRecentList: blogsRecentListReducer,
blogDetail: blogDetailReducer,
})
const initialState = {
}
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
)
export default store;
BlogModel
import mongoose from 'mongoose';
const blogSchema = mongoose.Schema({
title: {
type: String,
required: true
},
user:
{
id: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
name: String,
profilePic: String,
},
primaryImage: {
type: String,
required: true
},
secondaryImage: {
type: String,
required: true
},
category: {
type: String,
required: true
},
reviews: [reviewSchema],
rating: {
type: Number,
required: true,
default: 0,
},
numReviews: {
type: Number,
required: true,
default: 0,
},
featured: {
type: Boolean,
required: true,
default: false
},
paragraph1: {
type: String,
required: true
},
paragraph2: {
type: String,
required: true
},
paragraph3: {
type: String,
required: true
},
}, {
timestamps: true
})
const Blog = mongoose.model('Blog', blogSchema)
export default Blog
blogController
export const getBlogById = async (req, res) => {
try {
if (mongoose.Types.ObjectId.isValid(req.params.id)) {
const blog = await Blog.findById(req.params.id)
if (!blog) {
return res.status(404).json({ message: 'Blog not Found' })
} else {
res.json(blog)
}
} else {
return res.status(404).json({ message: 'Invalid ID' })
}
} catch (error) {
res.status(500).json({ message: 'Server Error' })
}
}
Blog Routes
import express from 'express'
const router = express.Router()
import { getBlogs, getBlogById, updateBlog, updateUserBlog, createBlog, deleteBlog, createBlogReview, getFeaturedBlogs, getRecentBlogs } from '../controllers/blogControllers.js';
import { admin, protect } from '../middleware/authMiddleware.js'
router.route('/').get(getBlogs).post(protect, createBlog)
router.route('/recent').get(getRecentBlogs)
// router.route('/top').get(getTopRatedBlogs)
router.route('/featured').get(getFeaturedBlogs)
// router.route('/category/:category').get(getBlogsByCategory)
router.route('/user/:id').put(protect, updateUserBlog)
router
.route('/:id')
.get(getBlogById)
.delete(protect, deleteBlog)
.put(protect, admin, updateBlog)
router.route('/:id/reviews').post(protect, createBlogReview)
export default router;
Server.js
import express from 'express'
import cors from 'cors'
import dotenv from 'dotenv'
import connectDB from './config/db.js'
import userRoutes from './routes/userRoutes.js'
import blogRoutes from './routes/blogRoutes.js'
dotenv.config()
connectDB()
const app = express()
app.use(cors({ credentials: true, origin: "http://localhost:3000" }))
app.use(express.json())
app.use('/api/users', userRoutes)
app.use('/api/blogs', blogRoutes)
app.get('/', (req, res) => {
res.send('Api is running....')
})
const PORT = process.env.PORT || 5000
app.listen(PORT, console.log(`Server is running on port ${PORT}`))