I am using React and react-router
. When I try to go to this route <Route path="/blogs/:id" element={<Blog/>} />
from the Blogs
page the whole content of the Blog
page content is blank and I have a lot of errors.
App.jsx
import Footer from "./components/Footer"
import Navbar from "./components/Navbar"
import Blogs from "./pages/Blogs"
import Cart from "./pages/Cart"
import Home from "./pages/Home"
import Shop from "./pages/Shop"
import { Routes, Route } from 'react-router-dom'
import './App.css'
import Blog from "./pages/Blog"
const App = () => {
return (
<>
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/shop" element={<Shop />} />
<Route path="/cart" element={<Cart />} />
<Route path="/blogs" element={<Blogs />} />
<Route path="/blogs/:id" element={<Blog/>} />
</Routes>
<Footer />
</>
)
}
export default App
Blogs.jsx
import { Link } from "react-router-dom"
import PageTitle from "../components/PageTitle"
import Data from '../data/Data.json'
const Blogs = () => {
return (
<section>
<PageTitle title={'Blogs'} />
<div className="container my-5">
<div className="row">
{Data.blogs.map((blog, i) => {
const { id, title, author, image, publishedDate, views } = blog
const truncatedTitle = title.length > 60 ? title.substring(0, 60) + '...' : title;
return (
<Link to={`/blogs/${id}`} key={i} className="col-12 col-sm-6 col-md-4 my-3" style={{ textDecoration: 'none' }}>
<div>
<div className="post-entry">
<div className="post-thumbnail">
<img src={image} alt="Image" className="img-fluid" />
</div>
<div className="post-content-entry">
<h6 className="my-2">{truncatedTitle}</h6>
<div className="meta">
<div>by {author}</div>
<div>on {publishedDate}</div>
<div><i className="fa-solid fa-eye ms-5"></i> {views}</div>
</div>
</div>
</div>
</div>
</Link>
)
})}
</div>
</div>
</section>
)
}
export default Blogs
Blog.jsx
import { useEffect, useState } from "react"
import { useParams } from "react-router-dom"
import Data from '../data/Data.json'
const Blog = () => {
const { id } = useParams()
const [blog, setBlog] = useState(null);
const { title, author, image, content, publishedDate, views } = blog
useEffect(() => {
const singleBlog = Data.blogs.find(item => item.id === parseInt(id))
if (singleBlog) {
setBlog(singleBlog)
}
}, []);
return (
<section>
<div className="container pt-5">
{blog ? (
<div className="blog">
<img src={image} height={'300px'}/>
<h2>{title}</h2>
<div>
<span>by {author}</span>
<span className="mx-5">on {publishedDate}</span>
<span><i className="fa-solid fa-eye ms-5"></i> {views}</span>
</div>
<p>{content}</p>
</div>
) : 'not found blog'}
</div>
</section>
)
}
export default Blog
2
Answers
The problem is that your
blog
state is initiallynull
, not anobject
sotitle
and the other properties can not be destructed from theblog
state.You can try to resolve it this way:
This way, you won’t try to obtain
blog.title
untilblog
is set to an object. There are two cases I added here, I am going to try to walk you through the process here:null
useEffect
runs but that is running in async mode.null
toblog
|undefined
undefined
, UI renders 404 page.Issue
the initial
blog
state value isnull
, and immediately after declaring theblog
state you attempt to destructure properties from this null value.You obviously can’t do this.
Solution
The UI should wait until
blog
is a defined object prior to attempting to destructure properties from it. It’s also a bit of a React anti-pattern to store derived "state" into React state. The current blog value is easily computed from theid
route path parameter and the imported JSON data.Additional things to keep in mind:
Array.prototype.find
returnsundefined
if no matching element is found.Example solution: