ReactJS: How to fetch data from API on button click - reactjs

I am making an app to fetch data from a recipeDB API, and right now I need to click on the search button have it console.log some data. Right now it is giving me a network error when attempting to fetch the resource. I have my code setup like this
import React, { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const API_ID = "0fc98505";
const API_KEY = "5e81ce76845f459b53f9fbe775e81e53";
const [recipes, setRecipes] = useState([]);
const getRecipes = async () => {
const response = await fetch(
`https://api.edamam.com/search?q=chicken&app_id=${API_ID}&app_key=${API_KEY}`
);
const data = await response.json();
setRecipes(data)
console.log(data);
};
return (
<div className="App">
<button onClick={getRecipes}>Retrive data</button>
</div>
);
}

If there is a network error, this means that your request is executed. I think the error here lies in your APP_ID and API_KEY. Another reason might be CORS. For more details visit MDN page.
Also you can check out «Network» tab in your developer tools(F12) where error description is sometimes shown in response body.

Related

Difficulties with useEffect and asyncawait

I've read several questions here regarding my current difficulty; they also told me the way I was coding it was wrong, and I changed it. However, even after changing I still can't seem to get the proper result.
I'm trying to make a small React HTTP Request app for learning purposes. According to the classes I've been following, I managed to create the json server, setup to watch for the DB.json properly, etc. Now, inside the App.js I'm trying to make the async\await call for listing the products in the console.
First, I had the following error:
"Effect callbacks are synchronous to prevent race conditions. Put the async function inside:"
I fixed it by changing my code. It was triggering a warning and I found out the classes I've been following are a bit outdate. No problem. However, even after changing it I can't view the products I create on db.json. If I go to localhost:3000/products it shows up there (which means things are working).
I believe I'm doing it the right way now, but I still can't seem to figure out why I can't view the data.
Any input is appreciated. Thanks!
ps: I'm just starting with react.
App.Js
import './App.css';
import { useState, useEffect } from "react";
const url="http:/localhost:3000/products";
function App() {
const [products, setProducts] = useState ([]);
useEffect(() => {
const fetchData = async () => {
const data = await fetch(url);
console.log("Data:" + data)
const res = await data.json();
console.log("Res:" + res)
setProducts(res);
}
fetchData();
}, []);
console.log(products);
return (
<div className="App">
<h1>LIsta de produtos</h1>
</div>
);
}
export default App;
The URL you put is missing a "/", Check if the URL is right, rest else seems to be correct, the code below should work.
import "./App.css";
import { useState, useEffect } from "react";
// URL is probably wrong, this is fixed URL
const url = "http://localhost:3000/products";
function App() {
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchData = async () => {
const data = await fetch(url);
console.log("Data:" + data);
const res = await data.json();
console.log("Res:" + res);
setProducts(res);
};
fetchData();
}, []);
console.log(products);
return (
<div className="App">
<h1>LIsta de produtos</h1>
</div>
);
}
export default App;

Why even after fetching the data and logging it, whenever I render it to the page after reloading the application breaks in React?

I am undertaking one of the projects from frontendmentor.io
Whenever I click any of the mapped data, with the help of react-router-dom I am redirecting it to url/countryName.
In this scenario, I am using react-router-dom to fetch a particular country while using the useLocation hook to fetch the url, the problem is everything works fine in the first render but as soon as I reload the website the application breaks. No matter how many times I try it never renders again.
import axios from 'axios'
import React,{ useEffect,useState } from 'react'
import { useNavigate, useLocation } from 'react-router-dom'
const Country = () => {
const location = useLocation()
const name = location.pathname.split('/')[1]
const navigate = useNavigate()
const [country,setCountry] = useState({})
useEffect(() => {
const getCountry = async() => {
try {
const res = await axios.get(`https://restcountries.com/v3.1/name/${name.toLowerCase()}?fullText=true`)
console.log(res.data,res)
setCountry(res.data)
} catch (error) {
console.log(error)
}
}
getCountry()
}, [name])
console.log(country[0].name.common) // returns undefined after reload
const backButton = (event) => {
event.preventDefault()
navigate('/')
}
return (
<div className='country-page page'>
<button onClick={backButton} className='backButton'>back button</button>
<div className='country-page-layout'>
<h2></h2>
</div>
</div>
)
}
export default Country
Other resources:
error message screenshot
API which I am using : https://restcountries.com/
You need to wait for your data to be fetched first, and then render it.
const [country,setCountry] = useState({})
//country is an empty object
//but here, your are trying to get the value from the array. (But country is an empty object)
console.log(country[0].name.common) // returns undefined after reload
The solution is to check if your data is here first
if (country?.[0]?.name?.common){
console.log(country[0].name.common)
}

how can I refetch with getServerSideProps in next on a click on client side?

I am using next.js, and trying to refresh the page with SSR data on a click of a button, doing like so:
import type { NextPage } from 'next'
import { useState } from 'react'
type HomeProps = NextPage & {
data: any
}
const Home = ({data}: HomeProps) => {
const [index, setIndex] = useState(data)
const handleClick = async() => {
const res = await fetch(`https://fakerapi.it/api/v1/companies?_quantity=2`)
const data= await res.json()
setIndex(data)
}
return (
<div>
{data.data.map(el => (
<div key={el.id}>{el.name}</div>
))}
<button onClick={handleClick}>next</button>
</div>
)
}
export async function getServerSideProps(){
const res = await fetch(`https://fakerapi.it/api/v1/companies?_quantity=1`)
const data= await res.json()
return{ props:{data}}
}
export default Home
I am getting the result of the first API call when next renders the page the first time, but when I click on the button, even though I am getting the result from the API call, the page does not refresh... Even thought I am using useState which should force the page to refresh.
Because of the way getServerSideProps works, you could refresh the data on the client-side using router object.
For example, when you click your button it could call a function to programmatically navigate to that same page using: router.replace(router.asPath).
This works because since getServerSideProps runs on every request, and you're already on the client-side and doing a navigation to a SSR page, instead of generating an HTML file, it will send the data as JSON to the client.
This is not a very good solution UX wise tho, but if used correctly it can be very handy.
oops my bad, i was not printing out the result of the useState, here is the proper change in the return of the function :
return (
<div>
{index.data.map(el => (
<div key={el.id}>{el.name}</div>
))}
<button onClick={handleClick}>next</button>
</div>
)

How to populate my const before the page renders?

I'm pretty new to ReactJS dev and today i'm trying to get some data from an API using Axios. My issue is :
I'm trying to de map function on resultData to get what i want inside, but an error is proped and it's showing : resultData.map is not a function
If i comment this part of the code and just render the page first, then uncomment, it works and data are shown.
I'm assuming that data is not loaded before the rendering process is over so that's why i get this. But how to make it load before ?
Here my code snippets :
import React, { useState, useEffect } from "react";
import "./App.css";
import axios from "axios";
const Url = "someAPi";
function App() {
const [baseData, setBaseData] = useState({});
const [resultData, setResultData] = useState({});
useEffect(() => {
getBaseDataWithAxios();
}, []);
const getBaseDataWithAxios = async () => {
const response = await axios.get(Url);
setBaseData(response.data);
};
useEffect(() => {
getResultDataWithAxios();
}, []);
const getResultDataWithAxios = async () => {
const response = await axios.get(Url);
setResultData(response.data.result);
};
const listItems =
resultData.map((d) => <li key={d.value}>{d.value}</li>);
return (
<div className="App">
<header className="App-header">
<h2>generated fees</h2>
</header>
<div className="info-container">
<h5 className="info-item">{baseData.status}</h5>
<h5 className="info-item">{baseData.message}</h5>
<h5 className="info-item">{listItems[1]}</h5>
</div>
</div>
);
}
export default App;
The error is thrown on this :
const listItems =
resultData.map((d) => <li key={d.value}>{d.value}</li>);
I know my data can be read since if i comment the listItems and the displaying part in the return, render the page, uncomment everything, it displays the data properly.
Can someone explain to me how to populate data first ? During my research i've seen that this can happen by using Axios.
Thanks a lot !
The useEffect hook always runs after your component function returns in the render cycle.
Try an empty array for your initial value of resultData instead of an empty object:
const [resultData, setResultData] = useState([]);
There is no map built-in method on non-array objects, so during the first execution, you receive that error.

why i'm not able to fetch data using axios call?

const [category, setCategory] = useState("general")
const news = await axios.get(`https://newsapi.org/v2/top-headlines?country=in&apiKey=64968be4903a4a979fe05c58a3355a73
&category=${category}`);
**As I am fetching API but its not fetching as is shows an empty array can anyone tell me where I am I going wrong **
You can not use the response of an async function directly in your React functional component. You have to use a state which holds your news. If you call setNews React automatically rerenders your component with the new news data.
export function News() {
const [category, setCategory] = useState("general");
const [news, setNews] = useState([]);
// fetch news everytime the category changes
useEffect(() => {
async function fetchNews() {
try {
const url = `https://newsapi.org/v2/top-headlines?country=in&apiKey=64968be4903a4a979fe05c58a3355a73&category=${category}`;
const response = await axios.get(url);
console.log(response);
setNews(response.data.articles);
} catch (errorWhileFetchingNews) {
console.log("error while fetching news", errorWhileFetchingNews);
}
}
fetchNews();
}, [category]);
// render the news
return (
<div>
{
news.map((article, i) => {
return <div key={i}>{article.title}</div>;
})
}
</div>
);
}
EDIT:
CAUTION: The CORS issues seem to appear only in my codesandbox example. If the example above runs on localhost:3000 in a normal React app (create-react-app) it works like it should. So you might ignore the following description.
Unfortunately the server newsapi.org doesn't send CORS headers. So you are not allowed to call this service directly via AJAX requests (axios, fetch, ...). You either find a way to enable CORS on this site (because you have an API key you may be able to administrate something?) or you find an other service that supports CORS or you have to send your request through a proxy. The proxy/backend then have to run on the same domain (host + port) like your frontend or the proxy must handle all the CORS header stuff. There are also questions on stackoverflow that have the same issue with newsapi.org but I am afraid that there is no easy solution/workaround for this.
I have setup a working example with jsonplaceholder.typicode.com (supports CORS) instead of newsapi.org.
See here: https://codesandbox.io/s/white-wildflower-su5vd?file=/src/News.js
Just in case the example is not reachable anymore, here is the code:
import { useState, useEffect } from "react";
import axios from "axios";
export function News(props) {
const [category, setCategory] = useState("general");
const [news, setNews] = useState([]);
// fetch news everytime the category changes
useEffect(() => {
async function fetchNews() {
try {
const url = "https://jsonplaceholder.typicode.com/comments";
const response = await axios.get(url);
console.log(response);
setNews(response.data);
} catch (errorWhileFetchingNews) {
console.log("error while fetching news", errorWhileFetchingNews);
}
}
fetchNews();
}, [category]);
// render the news
return (
<div>
{
news.map((article) => {
return <div key={article.id}>{article.name}</div>;
})
}
</div>
);
}

Resources