Unable to retrieve individual blog in react using axios - javascript

I have spent a couple of time trying to figure out why I'm not able to obtain individual blog post detail page using axios. The code does not return any data (It is returning undefined)
I have the follow code:
/public
/src
/components
/blog
BlogPosts.js
BlogDetail.js
...
App.js
import BlogDetail from './components/blog/BlogDetail';
The routing for the DETAIL_POST is:
<Route exact path='/blog/:id' component={BlogDetail} />
DETAIL_POST COMPONENT
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
export default const BlogDetail = (props) => {
const [blog, setBlog] = useState({});
useEffect(() => {
const slug = props.match.params.id;
const fetchData = async () => {
try {
const res = await axios.get(`https://example.com/blog/${slug}`);
setBlog(res.data);
}
catch (err) {
}
};
fetchData();
}, [props.match.params.id]);
const createBlog = () => {
return {__html: blog.body}
};
const capitalizeFirstLetter = (word) => {
if (word)
return word.charAt(0).toUpperCase() + word.slice(1);
return '';
};
return (
<div>
<div dangerouslySetInnerHTML={createBlog()} />
</div>
);
};
BlogPost COMPONENT
const Blog = () => {
const [blogs, setBlogs] = useState([]);
useEffect(() => {
const fetchBlogs = async () => {
try {
const res = await axios.get(`${process.env.REACT_APP_API_URL}/blog/post`);
setBlogs(res.data);
}
catch (err) {
}
}
fetchBlogs();
}, []);
const getBlogs = () => {
let list = [];
let result = [];
blogs.map(blogPost => {
return list.push(
<div className="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
<p className="card-text mb-auto">{blogPost.introduction}</p>
<Link to={`/blog/${blogPost.slug}`} className="stretched-link">Read More</Link>
</div>
);
});
for (let i = 0; i < list.length; i += 2) {
result.push(
<div key={i} className='row mb-2'>
<div className='col-md-6'>
{list[i]}
</div>
<div className='col-md-6'>
{list[i+1] ? list[i+1] : null}
</div>
</div>
)
}
return result;
};
return (
<div className="jumbotron p-4 p-md-5 text-white rounded bg-dark">
{getBlogs()}
</div>
);
};
export default Blog;
On checking the browser console I saw this error: Failed to load resource: the server responded with a status of 404 (Not Found) but I can't find where the error is because other components are returning data except the particular one.
The code returns data for those I practice with but never works in my case but everything seems to be similar to theirs.

Check the network tab to see the type of response which is fetched or if the request is made or not then make necessary changes.

The solution to the question happens to be simple, I couldn't have made such a mistake. In case anybody encounters similar issue and the frontend seems to work fine, here is the approach I took. I checked serializers.py in the backend app and saw that I did not add slug to the field even if I have slug in blog post models.py. Adding slug to fields in `serializers.py1 fixed the issue

Related

How to get the first elements of an array and then get the next few elements?

I'm building my blog page for my website and I have a posts folder with markdown files of my blogs. I'm just figuring out a way to display all the blogs on a page, but I want to optimize it a bit so it doesn't try to load all blog posts at once but only the first 6 for example. And then when you click on a Load More button the next 6 get loaded and displayed.
This is the code I'm using to get the data from my blog posts:
async function getBlogPosts(n: number) {
const files = fs.readdirSync('posts');
const posts = files.slice(0, n).map((fileName) => {
const slug = fileName.replace('.md', '');
const readFile = fs.readFileSync(`posts/${fileName}`, 'utf-8');
const { data: frontmatter } = matter(readFile);
return {
slug,
frontmatter,
};
});
return posts;
}
And then display the title of the posts:
export default async function Blogs() {
const posts = await getBlogPosts(6);
return (
<div className="mx-auto flex">
{posts.map(({ slug, frontmatter }) => (
<div
key={slug}
className="m-2 flex flex-col overflow-hidden rounded-xl border border-gray-200 shadow-lg"
>
<Link href={`/blog/${slug}`}>
<h3 className="p-4">{frontmatter.title}</h3>
</Link>
</div>
))}
</div>
);
}
How would one go about implementing this?
Because I think if I were to call GetBlogPosts(12) it would load 12 posts but also the first 6 which have already been loaded.
You code is perfect.
Just implement pagination during slice method, increase page number as user clicks show more. and slice REQUIRED FILES data only.
Your code would look like this:
async function getBlogPosts(blogsPerPage:number, pageNumber:number) {
const files = fs.readdirSync('posts');
!IMPORTANT. sort files array here before slicing, so we wont get repeated posts...
const posts = files.slice((pageNumber-1)* blogsPerPage,pageNumber*blogsPerPage).map((fileName) => {
const slug = fileName.replace('.md', '');
const readFile = fs.readFileSync(`posts/${fileName}`, 'utf-8');
const { data: frontmatter } = matter(readFile);
return {
slug,
frontmatter,
};
});
return posts;
}
Now you can implement a state in your functional component and call this function somewhat like this:
export default async function Blogs() {
const [pageNumber, setPageNumber] = useState(0);
const posts = await getBlogPosts(6,pageNumber);
......
}
import { useState } from 'react';
export default function Blogs() {
const [numPosts, setNumPosts] = useState(6); // number of posts to display
const [posts, setPosts] = useState([]); // array of blog post data
const loadMore = () => {
setNumPosts(numPosts + 6); // increase the number of posts to display by 6
};
// fetch the blog post data when the component mounts or when the number of posts to display changes
useEffect(() => {
async function fetchData() {
const files = fs.readdirSync('posts');
const data = files.map((fileName) => {
const slug = fileName.replace('.md', '');
const readFile = fs.readFileSync(`posts/${fileName}`, 'utf-8');
const { data: frontmatter } = matter(readFile);
return {
slug,
frontmatter,
};
});
setPosts(data);
}
fetchData();
}, [numPosts]);
return (
<div className="mx-auto flex flex-wrap">
{posts.slice(0, numPosts).map(({ slug, frontmatter }) => (
<div
key={slug}
className="m-2 flex flex-col overflow-hidden rounded-xl border border-gray-200 shadow-lg"
>
<Link href={`/blog/${slug}`}>
<h3 className="p-4">{frontmatter.title}</h3>
</Link>
</div>
))}
{numPosts < posts.length && (
<button onClick={loadMore} className="my-4 mx-auto bg-blue-500 text-white px-4 py-2 rounded">
Load More
</button>
)}
</div>
);
}

Add item from Fetch API to Array and Displaying new array in React

I'm learning react for the first time, I have an app where it fetches some data from a public API. I currently have it show 10 cards with random items from the API, and I have added a button to fetch a random item from the API and add it to the array, I managed to get the new item added to the array using push() but it does not show in the app itself. How can I make it that the new item is shown in the app as well?
Here is my code
Home.js
import { useState, useEffect} from "react";
import Card from './Card';
const Home = () => {
const [animals, setAnimals] = useState([]);
const handleDelete = (id) => {
const newAnimals = animals.filter(animal => animal.id !== id);
setAnimals(newAnimals);
}
useEffect(() => {
fetch('https://zoo-animal-api.herokuapp.com/animals/rand/10')
.then(res => {return res.json()})
.then(data => {
setAnimals(data);
});
}, []);
const handleAddAnimal = () => {
fetch('https://zoo-animal-api.herokuapp.com/animals/rand/')
.then(res => {return res.json()})
.then(data => {
animals.push(data);
console.log(animals);
//what to do after this
})
}
return (
<div className="home">
<h2>Animals</h2>
<button onClick={handleAddAnimal}>Add Animal</button>
<Card animals={animals} handleDelete={handleDelete}/>
</div>
);
}
export default Home;
Card.js
const Card = ({animals, handleDelete}) => {
// const animals = props.animals;
return (
<div className="col-3">
{animals.map((animal) => (
<div className="card" key={animal.id}>
<img
src={animal.image_link}
alt={animal.latin_name}
className="card-img-top"
/>
<div className="card-body">
<h3 className="card-title">{animal.name}</h3>
<p>Habitat: {animal.habitat}</p>
<button onClick={() => handleDelete(animal.id)}>Delete Animal</button>
</div>
</div>
))}
</div>
);
}
export default Card;
App.js
import Navbar from './navbar';
import Home from './Home';
function App() {
return (
<section id="app">
<div className="container">
<Navbar />
<div className="row">
<Home />
</div>
</div>
</section>
);
}
export default App;
Screenshot of what I see now
screenshot
(I was also wondering how to fix the items going down instead of side by side but wanted to fix the add button first)
Let me know if there's anything else I should add, any help is appreciated, thank you!
Rather using array.push() method. You try using
setTheArray([...theArray, newElement]); e.g in your case it will be setAnimals([...animals,data]) in your onClick event.
Let me know doest it solve your issue or not.
const handleAddAnimal = () => {
fetch('https://zoo-animal-api.herokuapp.com/animals/rand/')
.then(res => {return res.json()})
.then(data => {
setAnimals([...animals,data])
console.log(animals);
//what to do after this
})
}

How to make a loading screen on react router?

I start learning react about 2 month ago. Right now I am trying to build my portfolio with some interactive design using spline 3d. The problem is the loading time is too long and I want to make a loading screen that stop loading exact time when my 3d start element render
There are multiple ways to create it by your self.
you can you use the library react-loader-spinner
on the console type npm install react-loader-spinner --save
import React from 'react';
import "react-loader-spinner/dist/loader/css/react-spinner-loader.css";
import Loader from "react-loader-spinner";
import '../style.css';
const LoaderComponent = () => {
return (
<div className="loader">
<Loader
type="Circles"
color="#dc1c2c"
height={50}
width={100}
//timeout={1000} //3 secs
/>
</div>
);
};
export default LoaderComponent;
To display the component there are multiple ways, here is a way for GraphQL fetching data from the DB
const [results] = useQuery({ query: PRODUCT_QUERY });
const { data, fetching, error } = results;
//Check or the data coming in
if (fetching) return <p>Loading...</p>;
if (error) return <p>Oh no... {error.message}</p>;
Here is a way from fetching data with HTTP Request:
const UserList = () => {
const auth = useContext(AuthContext);
const { isLoading, error, sendRequest, clearError } = useHttpClient();
const [loadedUsers, setLoadedUsers] = useState();
useEffect(() => {
const fetchUsers = async () => {
try {
//with fetch, the default request type is GET request
const responseData = await sendRequest(
process.env.REACT_APP_BACKEND_URL + "/users"
);
setLoadedUsers(responseData.users); //users propeties is the given value from the backend (user-controllers.js on getUsers())
} catch (err) {}
};
fetchUsers();
}, [sendRequest]);
return (
<React.Fragment>
<ErrorModal error={error} onClear={clearError} />
{isLoading && <LoadingSpinner asOverlay />}
{/* we need to render loadedUsers only if not empty*/}
{!isLoading && loadedUsers && (
<div className="userList">
<span className="Title">Display Here the data</span>
</div>
)}
</React.Fragment>
);
};
// this logic is simple
// first, you have created one boolean usestate(false) and then load your screen that time usestate are true and process is complete after usesate are false
// I will show you the following example. I hope that helps you.
export default function Gradients(props) {
const [isLoading, setIsLoading] = useState(false);
const getAllGradient = () => {
setIsLoading(true);
axios
.get("https://localhost:5000")
.then((res) => {
const gradientColors = res.data;
// process complete after isLoading are false
// your process (this only example)
setIsLoading(false);
})
}
return(
<div>
{
isLoading ? <Loader> : <YourComponent />
}
</div>
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

FetchError: invalid json response body , reason: Unexpected token < in JSON at position 0

i am trying to show up the single blog page in my next project.
i get the error at getStaticProps
i tried make the data that is coming from the api an array but it doesn't work also , i don't know where the issue of this code everything looks fine
i don't know if the error from the api or something else.
import { useRouter } from 'next/router';
import Link from 'next/link';
import BlogsStyle from '../../../styles/Blogs.module.css';
import { Image } from "react-bootstrap";
const Blog = ({ blog }) => {
const router = useRouter()
const {id} = router.query
return (
<div>
<div className={BlogsStyle.blogItem}>
<div className={BlogsStyle.blogImg}>
<Image className={BlogsStyle.blogImgSrc} src={blog.image_url} alt="image blog" onError={(e)=>{e.target.onerror = null; e.target.src="../../public/images/default_blog.png"}} fluid />
</div>
<div className=" blog__item--text">
<div className="info">
<div className="date">{blog.publish_date}</div>
<h6>{blog.title.ar}</h6>
<p>{strip(blog.content.ar).substring(0,100)}</p>
</div>
</div>
</div>
</div>
)
}
export const getStaticPaths = async () => {
const res = await fetch('https://masahefapi.slsal.co/pgarticles/articles/0/10');
const data = await res.json();
console.log(data);
const paths = await data.map(blog =>{
return {
params: { id: blog.id.toString() }
}
})
return {
paths,
fallback: false
}
}
export const getStaticProps = async({ params }) => {
const id = context.params.id;
const res = await fetch(`https://masahefapi.slsal.co/pgarticles/articles/0/10/${params.id}`);
const data = await res.json();
console.log(data);
return {
props: {
blog: data
}
}
}
export default Blog

Where to place a variable in a React component?

Error: "Type error Object(...) is not a function"
I'm trying to debug this line of code that I want to iterate over, but it does not work:
{
shuffleAndPick(images, 3).map((image) => (
<div className="key" key={image.id}>
<h3>{image.name}</h3>
<h3>{image.sanskritname}</h3>
<p>{image.description}</p>
<img src={image.image} alt={image.name} />
</div>
))
}
I want to do the debugging by setting the shuffleAndPick() function with a value into a variable. And then console.log() the variable to see what I get. In other words, I want to do something like this:
const shuffledArray = shuffleAndPick(images, 3)
console.log(shuffledArray)
shuffleAndPick
const shuffleAndPick = (array, amount) => {
return array.sort(() => 0.5 - Math.random()).slice(0, amount);
};
My problem is that I dont know where in my code I should put the variable and the console.log.And I only get errors. The code that I want to debug looks like this:
import React, { useState, useEffect, useParams } from 'react'
import { useHistory } from 'react-router-dom'
import { shuffleAndPick } from '../helpers/shuffleAndPick'
const URL = 'http://localhost:8080/chakra'
export const Energy = () => {
const [images, setImages] = useState([]);
const history = useHistory()
const fetchSolarPlexus = () => {
fetch('http://localhost:8080/chakra/5e6c096afe1b75409f5c6133/asana')
.then (res => res.json())
.then((json) => {
setImages(json)
})
}
useEffect(() => {
fetchSolarPlexus()
}, []);
return (
<section className="WorkOut">
<h2>Energy</h2>
{shuffleAndPick(images, 3).map((image) => (
<div className="key" key={image.id}>
<h3>{image.name}</h3>
<h3>{image.sanskritname}</h3>
<p>{image.description}</p>
<img src={image.image} alt={image.name} />
</div>
))}
<button onClick={() => history.goBack()} className="backLink">
Back
</button>
</section>
)
}
Where should I place the variable I want to console.log?
The answer to my question is to but the test variable before the return.
I also got alot of help debugging.
Se the comments for more info.

Categories

Resources