Intro
I had created a NewsReaderApp which is working fine in the local environment using newsAPI.
Problem
The problem is occurring in the deployment part, it is deployed successfully in Netlify but when I open the deployed site click here , it is showing no news articles,
ideally, it should look like this picture below
.
This means that the API is not fetching the data. I don't know why it is happening. Even I tried to console log the parsedData I am not able to see the consoled stmt in the local as you can see in the pic also. I am sharing the code of the file where I am fetching the data.
code
News.js
import React, { useState,useEffect } from "react";
import NewItem from "./NewItem";
import Spinner from "./Spinner";
import InfiniteScroll from "react-infinite-scroll-component";
const News = (props) => {
const capitilizeFirstLetter = (str) => {
return str.charAt(0).toUpperCase() + str.slice(1);
};
const [articles, setArticles] = useState([]);
const [loading, setLoading] = useState(true);
const [page, setPage] = useState(1);
const [totalResults, setTotalResults] = useState(0);
const updateNews = async() => {
props.setProgress(10);
const url = `https://newsapi.org/v2/top-headlines?country=${props.country}&category=${props.category}&apiKey=${props.apikey}&page=${page}&pageSize=${props.pageSize || "12"}`;
setLoading(true);
let data = await fetch(url);
props.setProgress(30);
let parsedData = await data.json();
props.setProgress(50);
//console.log(parsedData)
setArticles(parsedData.articles);
setTotalResults(parsedData.totalResults);
setLoading(false);
props.setProgress(100);
}
useEffect(() => {
updateNews();
document.title = `NewsReader2 - ${capitilizeFirstLetter(props.category)}`;
// eslint-disable-next-line react-hooks/exhaustive-deps
//console.log("render");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const fetchMoreData = async () => {
let url = `https://newsapi.org/v2/top-headlines?country=${props.country || "in"}&category=${props.category || "general"}&apiKey=${props.apikey}&page=${page+1}&pageSize=${props.pageSize}`;
setPage(page+1);
let data = await fetch(url);
let parsedData = await data.json();
console.log(parsedData)
setArticles(articles?.concat(parsedData?.articles));
setTotalResults(parsedData.totalResults);
};
return (
<div style={props.darkmodeStyle}>
<h1 className="text-center" style={{ margin: "35px 0px", marginTop:'5%' }}>
New Reader2
</h1>
<div
className={`alert alert-${props.mode === 'light' ? 'primary':'custom'} text-center`}
role="alert"
style={{ fontSize: "150%", fontWeight: 700 }}
>
News from {capitilizeFirstLetter(props.category)} world
</div>
{loading && <Spinner />}
<InfiniteScroll
next={fetchMoreData}
dataLength={
articles?.length ? articles?.length : 15
}
hasMore={articles?.length !== totalResults}
loader={<Spinner />}
>
<div className="container">
<div className="row">
{articles?.map((item) => {
return (
<div className="col-md-4" key={item.url}>
<NewItem
title={item.title ? item.title.slice(0, 45) : ""}
description={
item.description ? item.description.slice(0, 88) : ""
}
imageUrl={
item.urlToImage
? item.urlToImage
: "https://www.eastmojo.com/wp-content/uploads/2021/08/space-pen-5.jpg"
}
newsUrl={item.url}
publishedAt={item.publishedAt}
author={item.author ? item.author : "Anonymous"}
source={item.source.name}
darkmodeStyle={props.darkmodeStyle}
/>
</div>
);
})}
</div>
</div>
</InfiniteScroll>
</div>
);
}
export default News;
Please help me to find a way to deploy this app successfully?
You have CORS issue. The API you are using worked on development because the domain localhost is allowed. See the image below
The error message is : "Requests from the browser are not allowed on the Developer plan, except from localhost."
Check out the API's docs to see what you can do. It seems that you need to pay in order to deploy your website.
Related
I have a hook that rules my requests. It has state "loading" that becoming true while loading
export const useHttp = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const request = useCallback(async (url, method = 'GET', body = null, headers = {'Content-Type': 'application/json' }) => {
setLoading(true);
try {
const res = await fetch(url, {method, body, headers});
if (!res.ok) {
throw new Error(`Could not fetch ${url}, status: ${res.status}`);
}
const data = await res.json();
setLoading(false);
return data;
} catch(e) {
setLoading(false);
setError(e.message);
throw e;
}
}, [])
const clearError = useCallback(() => {
setError(null);
}, [])
return {
loading,
error,
request,
clearError
}
}
Also i have a service that makes requests:
import { useHttp } from "../hooks/http.hook";
const useNasaService = () => {
const { loading, error, request, clearError } = useHttp();
const _apiBase = 'https://api.nasa.gov/';
const _apiKey = 'api_key=DEMO_KEY';
const getMissionManifest = async (rover) => {
const res = await request(`${_apiBase}mars-photos/api/v1/manifests/${rover}/?${_apiKey}`);
return _transformManifestData(res.photo_manifest);
}
const getImagesData = async (rover, sol, page = 1) => {
const res = await request(`${_apiBase}mars-photos/api/v1/rovers/${rover}/photos?sol=${sol}&page=${page}&${_apiKey}`);
return res.photos.map(_transformImagesData);
}
const _transformImagesData = (data) => {
return {
id: data.id,
sol: data.sol,
earthDate: data.earth_date,
path: data.img_src,
camera: data.camera.full_name,
rover: data.rover.name
}
}
const _transformManifestData = (data) => {
return {
landingDate: data.landing_date,
launchDate: data.launch_date,
maxDate: data.max_date,
maxSol: data.max_sol,
name: data.name,
photos: data.photos,
status: data.status,
totalPhotos: data.total_photos
}
}
return {
loading,
error,
clearError,
getImagesData,
getMissionManifest
}
}
export default useNasaService;
Finally i have a component that needs state "loading" for disabling the inputs.
The question is why "loading" is never getting true in this component:
import useNasaService from '../../services/useNasaService';
const RoverFilter = (props) => {
const { loading } = useNasaService();
console.log(loading); /* always false */
const onRadioChange = (e) => {
props.onRoverSelected(e.target.value);
props.onRoverClicked(e.target.value);
}
return (
<div className="roverFilter" >
<h2 className="roverFilter__title">Select rover</h2>
<div className="roverFilter__inputs">
<label htmlFor="curiosity">Curiosity</label>
<input disabled={loading} type="radio" name="rover-choise" id="curiosity" value="curiosity" onChange={onRadioChange}/>
<label htmlFor="opportunity">Opportunity</label>
<input disabled={loading} type="radio" name="rover-choise" id="opportunity" value="opportunity" onChange={onRadioChange}/>
<label htmlFor="spirit">Spirit</label>
<input disabled={loading} type="radio" name="rover-choise" id="spirit" value="spirit" onChange={onRadioChange}/>
<label htmlFor="perseverance">Perseverance</label>
<input disabled={loading} type="radio" name="rover-choise" id="perseverance" value="perseverance" onChange={onRadioChange}/>
</div>
</div>
)
}
export default RoverFilter;
By the way, in my app there are another components, where "loading" becoming true without any problems. I cant see the difference.
for example, here loading works good:
import { useEffect, useState } from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import useNasaService from '../../services/useNasaService';
import ImageGallerySkeleton from '../imageGallerySkeleton/ImageGallerySkeleton';
import Spinner from '../spinner/Spinner';
import ErrorMessage from '../errorMessage/ErrorMessage';
import SliderModal from '../sliderModal/SliderModal';
const ImageGallery = (props) => {
const {loading, getImagesData, clearError, error} = useNasaService();
const [imagesData, setImagesData] = useState([]);
const [nextPage, setNextPage] = useState(1);
const [firstLoading, setFirstLoading] = useState(true);
const [imagesDataLoaded, setImagesDataLoaded] = useState(false);
const [itemIndex, setItemIndex] = useState(0);
const [sliderOpen, setSliderOpen] = useState(false);
const transitionDuration = 1000;
const onImagesDataLoaded = (newData) => {
setImagesData(data => [...data, ...newData]);
setNextPage(page => page + 1);
setFirstLoading(false);
setImagesDataLoaded(true);
}
const onRequestImages = (rover, sol, page) => {
clearError();
if (!rover || !sol) return;
getImagesData(rover, sol, page)
.then(onImagesDataLoaded);
}
const onSliderClosed = () => {
setSliderOpen(false);
}
useEffect(() => {
onRequestImages(props.selectedRover, props.selectedSol, nextPage);
// eslint-disable-next-line
}, [props.selectedRover, props.selectedSol])
if (sliderOpen) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "visible";
}
function renderItemList(arr) {
const itemList = arr.map((item, i) => {
return (
<CSSTransition
key={item.id}
in={imagesDataLoaded}
timeout={transitionDuration}
classNames='imageGallery__card'>
<li className="imageGallery__card"
onClick={() => {
setSliderOpen(true);
setItemIndex(i);
}}>
<img src={item.path} alt="img from mars"/>
<div className="imageGallery__descr">
<ul>
<li>Rover: {item.rover}</li>
<li>Earth_date: {item.earthDate}</li>
<li>Sol: {item.sol}</li>
<li>{item.camera}</li>
</ul>
</div>
</li>
</CSSTransition>
)
})
return (
<ul className="imageGallery__list">
<TransitionGroup component={null}>
{itemList}
</TransitionGroup>
</ul>
)
}
const spinner = loading && firstLoading ? <Spinner/> : null;
const skeleton = imagesData.length === 0 && firstLoading && !loading && !error ? <ImageGallerySkeleton/> : null;
const items = renderItemList(imagesData);
const errorMessage = error ? <ErrorMessage/> : null;
const counter = imagesData.length === 0 || error ? null :
<h2 className="imageGallery__title">
Showed {loading ? "..." : imagesData.length} photos of {props.totalPhotosInSol}
</h2>
const button = props.totalPhotosInSol === imagesData.length ? null :
<button
onClick={() => onRequestImages(props.selectedRover, props.selectedSol, nextPage)}
disabled={loading}
className="imageGallery__btn">{loading ? "Loading..." : "Load next page" }
</button>
const slider = <SliderModal
open={sliderOpen}
items={imagesData}
slideIndex={itemIndex}
onSliderClosed={onSliderClosed} />
const wrapStyles = firstLoading && loading ? {"padding": "50px"} : null;
return (
<section className="imageGallery" style={wrapStyles}>
{counter}
{spinner}
{skeleton}
{imagesData.length === 0 && !firstLoading ?
<h2 className="imageGallery__title">There is no photo for this sol</h2> :
items
}
{button}
{errorMessage}
{slider}
</section>
)
}
export default ImageGallery;
When you call useState in two different components, those states are independant from eachother. This is still true if you move the useState calls inside a custom hook. If two components call useNasaService (which calls useHttp, which calls useState), then the two components are creating their own states and own functions. If component A starts loading data, that will have no effect on component B.
So ImageGallery is working because it makes a call to getImagesData. This sets the loading state of ImageGallery to true. No other components are affected by this though. When the loading finishes, ImageGallery will set state to have the new data, but again, no other components can use this. RoverFilter on the other hand never calls getImagesData, so its loading state stays false, and it never gets any data.
In react, the typical way to share data is to lift state up. You have a component higher up in the tree, which is responsible for loading the data and setting state. That component then passes the data and functions down to any children that need it. You can either pass the data down using props, or if you need to pass the data a long distance you can consider using context instead.
There's also a wide variety of 3rd party libraries which can be used to manage global state. For example, Redux, Jotai, Zustand, MobX. These can make it simpler to share data between components in far-flung parts of the component tree.
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>
I'm using react-intersection-observer npm package for my CRA project.
I want to achieve this Infinite Scroll effect whenever users reach the bottom of the page or click on the Load More button -> It will fetch 8 more items.
Currently, the getMoreData() will only execute when I click on the Load More button.
Also, I put posts state in the dependency array so it will display the first 8 items for activePosts on start. But this leads to status code 429: over rate limit in the Network tab.
My Questions:
How to also apply the getMoreData() function to fetch out more items when we scroll to the end of the page? (Infinity scroll)
There might be a bug if I remove the posts from the dependency array, it won't display the first 8 items on initial page load. How to fix this the right way?
Screenshots:
My Code:
Posts.js
function Posts() {
const url = "https://6264f60294374a2c506b97c9.mockapi.io/posts";
const [posts, setPosts] = useState([]);
const [activePosts, setActivePosts] = useState([]);
const [isFetching, setIsFetching] = useState(false);
const getData = async () => {
try {
let response = await axios(url);
let result = response.data;
setPosts(result);
setActivePosts(posts.slice(0, 8));
} catch (err) {
console.log(err);
}
};
useEffect(() => {
getData();
}, [posts]);
const getMoreData = () => {
setIsFetching(true);
setTimeout(() => {
setActivePosts((prev) => {
return [...prev, ...posts.slice(prev.length + 1, prev.length + 9)];
});
setIsFetching(false);
}, 2000);
};
useEffect(() => {
if (!isFetching) return;
getMoreData();
}, [isFetching]);
return (
<>
<div className="posts">
{activePosts.map((post, index) => (
<Post post={post} key={post.id} index={index} />
))}
</div>
<button onClick={getMoreData}>
{isFetching ? "Loading..." : "Load more"}
</button>
</>
);
}
export default Posts;
Post.js
import { useInView } from "react-intersection-observer";
function Post({ post }) {
const { ref, inView } = useInView({
initialInView: true,
triggerOnce: true,
threshold: 1,
});
return (
<div className="post" ref={ref}>
{inView ?
<img src={post.imgUrl} alt={post.title} className="post__img" loading="lazy" />
: <div className="post__img" />
}
<h1 className="post__title">{post.title}</h1>
</div>
)
}
export default Post
I am working on react website.
I have created one custom data fetching hook 'usePostFetch' as follows:
import React, { useState, useEffect } from "react";
//axios
import axios from "axios";
const usePostFetch = () => {
const [postData, setPostData] = useState([]);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const getData = async () => {
setIsLoading(true);
try {
const res = await axios.get("http://localhost:8000/Sell");
const data = await res.data;
setPostData(data);
setIsLoading(false);
} catch (error) {
console.log("Error from fetch: " + error);
setError(error.message);
setIsLoading(false);
}
};
getData();
}, []);
const values = [
...new Set(
postData.map((post) => {
return post.productType;
})
),
];
return { postData, values, error, isLoading };
};
export default usePostFetch;
I have a product page that renders when I click any of the links on the home page with a link "/product/:productId".productId is the id of clicked link product.
Product Page:
import React, { useEffect, useState } from "react";
//react router dom
import { useParams } from "react-router";
//Hooks
import usePostFetch from "../../Hooks/usePostFetch";
//styles
import { Wrapper, Info, Discription } from "./Product.styles";
//Server
const Server = "http://localhost:8000";
const Product = () => {
const { productId } = useParams();
const { postData, isLoading, error } = usePostFetch();
const [data, setData] = useState({});
console.log(postData, isLoading, error);
useEffect( () => {
const fetchData = async () => {
var value = await postData.filter((post) => {
return post._id === productId;
});
console.log(value);
setData(value);
};
fetchData();
}, [postData]);
return (
<Wrapper>
<Info>
{isLoading && <h1> Loading.... </h1>}
{error && <p>ERROR </p>}
{console.log(data)}
<img
src={`${Server}/productImages/${data[0].productImage}`}
alt={`${data[0].productName}`}
/>
<div className="data">
<h1>{data[0].productName}</h1>
<h3>{data[0].productPrice}</h3>
</div>
</Info>
</Wrapper>
);
};
export default Product;
But when I go to that link I got data in console like this:
Because of these empty arrays, I got errors like this:
What can I do or what is wrong with my code?
It appears you are reading state that doesn't exist yet. The initial data state is an empty object:
const [data, setData] = useState({});
And on the initial render you are attempting to read from a 0 property, which OFC is undefined still.
data[0] --> OK, undefined
data[0].productName --> NOT OK, throws error trying to access from undefined
You can conditionally render the data content when you know it's populated:
<Wrapper>
<Info>
{isLoading && <h1> Loading.... </h1>}
{error && <p>ERROR </p>}
{console.log(data)}
{data[0] && (
<img
src={`${Server}/productImages/${data[0].productImage}`}
alt={`${data[0].productName}`}
/>
<div className="data">
<h1>{data[0].productName}</h1>
<h3>{data[0].productPrice}</h3>
</div>
)
</Info>
</Wrapper>
Or you can just use the Optional Chaining operator to defend against null/undefined property accesses:
<Wrapper>
<Info>
{isLoading && <h1> Loading.... </h1>}
{error && <p>ERROR </p>}
{console.log(data)}
<img
src={`${Server}/productImages/${data[0]?.productImage}`}
alt={`${data[0]?.productName}`}
/>
<div className="data">
<h1>{data[0]?.productName}</h1>
<h3>{data[0]?.productPrice}</h3>
</div>
</Info>
</Wrapper>
It also seems that you are really expecting data to be an array, so you will want your initial state to maintain a state/type invariant, so it should also be declared as an array.
const [data, setData] = useState([]);
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