i have been tiring to solve this problem for two days but no luck This is my first project with react v18.1, basically I'm stuck with single post, Going from blog list to single post Loads fine, but if I refresh (Browser refresh button) single post or try to access to it directly I get blank page
It's Like the data won't load unless It passes through Blog List.
This is my Route from index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route } from "react-router-dom";
import './index.css';
import './global.css';
import App from './App';
import About from './components/About';
import Contact from './components/Contact';
import NotFound from './components/NotFound';
import Blog from './components/Blog';
import Doctors from './components/Doctors';
import registerServiceWorker from './registerServiceWorker';
import Singlepost from './components/singlepost';
import Home from './components/Home';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/" element={<App />} >
<Route path="Doctors" element={<Doctors />} />
<Route path="Blog" element={<Blog />} />
<Route path="blog/:articleId" element={<Singlepost />} />
<Route path="About" element={<About />} />
<Route path="Contact" element={<Contact />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</BrowserRouter>
);
registerServiceWorker();
Code from App.js
import { useState, useEffect } from "react";
import Header from "./components/header/header";
import Footer from "./components/footer";
import { Outlet } from "react-router-dom";
function App() {
// storing the current page number and data in state
const [blogData, setBlogData] = useState([]);
const [url, setUrl] = useState(`${process.env.PUBLIC_URL}/blogpost.json`);
// Fetching data from json file
const dataFetch = async () => {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
const data = await response.json();
setBlogData(data);
}
useEffect(() => {
dataFetch()
}, []);
return (
<>
<Header />
<div className="flex flex-col my-20">
<Outlet context={[blogData]} />
</div>
<Footer />
</>
);
}
export default App;
Code from blog.js page
import React, { useState, useMemo, useEffect } from 'react'
import { Link, Outlet, useOutletContext, } from 'react-router-dom';
import Pagination from './Pagination';
let PageSize = 10;
const MAX_LENGTH = 500;
export default function Blog() {
const [articles] = useOutletContext();
const [currentPage, setCurrentPage] = useState(1);
// pagination relation function
let currentTableData = useMemo(() => {
const firstPageIndex = (currentPage - 1) * PageSize;
const lastPageIndex = firstPageIndex + PageSize;
return articles.slice(firstPageIndex, lastPageIndex);
}, [currentPage, articles]);
function slugify(text) {
return text.toString().toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, ''); // Trim - from end of text
}
return (
<>
{
currentTableData.map(doc => (
<div key={crypto.randomUUID()} className="flex flex-col md:flex-row w-full m-auto mb-4 shadow rounded border">
<div>
<img src={doc.Image_URL} alt="doctor" className="w-full h-64 md:h-auto md:w-64 rounded-l md:mr-4 max-h-[200px]" />
</div>
<div className='flex flex-col'>
<Link to={`/blog/${doc.ID}`} className='font-semibold'>{doc.Title}</Link>
<div className='flex flex-col'>
{doc.Content.length > MAX_LENGTH ?
(
<div className='prose lg:prose-xl' dangerouslySetInnerHTML={{ __html: doc.Content.substring(0, MAX_LENGTH) }}>
</div>
) :
<p className='prose lg:prose-xl' dangerouslySetInnerHTML={{ __html: doc.Content }} ></p>
}
<span>{doc.Category}</span>
</div>
</div>
</div>
))
}
<Outlet context={[articles]} />
<Pagination
key={crypto.randomUUID()}
data-link={crypto.randomUUID()}
className="pagination-bar"
currentPage={currentPage}
totalCount={articles.length}
pageSize={PageSize}
onPageChange={page => setCurrentPage(page)}
/>
</>
)
}
SinglePost.js
import React from 'react'
import { useOutletContext, useParams } from 'react-router-dom';
export default function Singlepost() {
let params = useParams();
const [blogData] = useOutletContext();
const blogPost = blogData.find(blog => blog.ID === parseInt(params.articleId));
return (
<div className="flex flex-col md:flex-row w-full m-auto mb-4 shadow rounded border">
<div>
<img src={blogPost.Image_URL} alt="doctor" className="w-full h-64 md:h-auto md:w-64 rounded-l md:mr-4 max-h-[200px]" />
</div>
<div className='flex flex-col'>
<h2 className='font-semibold'>{blogPost.Title}</h2>
<div className='flex flex-col'>
<div className='prose lg:prose-xl' dangerouslySetInnerHTML={{ __html: blogPost.Content }}></div>
<span>{blogPost.Category}</span>
</div>
</div>
</div>
)
}
Error after hard refresh
singlepost.js:17 Uncaught TypeError: Cannot read properties of undefined (reading 'Image_URL')
at Singlepost (singlepost.js:17:1)
at renderWithHooks (react-dom.development.js:16175:1)
at mountIndeterminateComponent (react-dom.development.js:20913:1)
at beginWork (react-dom.development.js:22416:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4161:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4210:1)
at invokeGuardedCallback (react-dom.development.js:4274:1)
at beginWork$1 (react-dom.development.js:27405:1)
at performUnitOfWork (react-dom.development.js:26513:1)
at workLoopSync (react-dom.development.js:26422:1
--------------------------------------------------------------
singlepost.js:17 Uncaught TypeError: Cannot read properties of undefined (reading 'Image_URL')
at Singlepost (singlepost.js:17:1)
at renderWithHooks (react-dom.development.js:16175:1)
at mountIndeterminateComponent (react-dom.development.js:20913:1)
at beginWork (react-dom.development.js:22416:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4161:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4210:1)
at invokeGuardedCallback (react-dom.development.js:4274:1)
at beginWork$1 (react-dom.development.js:27405:1)
at performUnitOfWork (react-dom.development.js:26513:1)
at workLoopSync (react-dom.development.js:26422:1)
-------------------------------------------------------------------
react-dom.development.js:18572 The above error occurred in the <Singlepost> component:
at Singlepost (http://localhost:3000/static/js/bundle.js:1857:75)
at Outlet (http://localhost:3000/static/js/bundle.js:68128:26)
at div
at App (http://localhost:3000/static/js/bundle.js:38:82)
at Routes (http://localhost:3000/static/js/bundle.js:68220:5)
at Router (http://localhost:3000/static/js/bundle.js:68153:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:66962:5)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
---------------------------------------------------------------------------
Uncaught TypeError: Cannot read properties of undefined (reading 'Image_URL')
at Singlepost (singlepost.js:17:1)
at renderWithHooks (react-dom.development.js:16175:1)
at mountIndeterminateComponent (react-dom.development.js:20913:1)
at beginWork (react-dom.development.js:22416:1)
at beginWork$1 (react-dom.development.js:27381:1)
at performUnitOfWork (react-dom.development.js:26513:1)
at workLoopSync (react-dom.development.js:26422:1)
at renderRootSync (react-dom.development.js:26390:1)
at recoverFromConcurrentError (react-dom.development.js:25806:1)
at performConcurrentWorkOnRoot (react-dom.development.js:25706:1)
I appreciates any thoughts. Thank you.
You are forgetting that array.prototype.find
returns undefined
when no match is found in the array.
const blogPost = blogData.find(blog => blog.ID === parseInt(params.articleId));
In this case it's because the blogData
array is empty and there is no match to be found.
This alone isn't what causes the problem, it's only the first step in leading to thrown errors. The error is thrown when later trying to render the found result and attempting to access into an undefined value.
<img
src={blogPost.Image_URL} // <-- can't access Image_URL of undefined!
alt="doctor"
className="......"
/>
There are a few things you can do to improve the code.
Check that there is a renderable blogPost
object. Conditionally render the single post UI.
export default function Singlepost() {
const { articleId } = useParams();
const [blogData] = useOutletContext();
const blogPost = blogData.find(blog => String(blog.ID) === articleId);
if (!blogPost) return null;
return (
... blogPost ...
);
}
Expose the dataFetch
in the context so blog posts can be fetched from children if necessary. They can render some loading indicator if desired.
App
function App() {
// storing the current page number and data in state
const [blogData, setBlogData] = useState([]);
const [url, setUrl] = useState(`${process.env.PUBLIC_URL}/blogpost.json`);
// Fetching data from json file
const dataFetch = async () => {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
const data = await response.json();
setBlogData(data);
}
useEffect(() => {
dataFetch();
}, []);
return (
<>
<Header />
<div className="flex flex-col my-20">
<Outlet context={{ blogData, dataFetch }} />
</div>
<Footer />
</>
);
}
SinglePost
export default function Singlepost() {
const { articleId } = useParams();
const { blogData, dataFetch } = useOutletContext();
const blogPost = blogData.find(blog => String(blog.ID) === articleId);
useEffect(() => {
if (!blogPost) {
dataFetch();
}
}, [blogPost, dataFetch]);
if (!blogPost) return null; // or loading spinner/etc...
return (
... blogPost ...
);
}