React Hooks, API, Axios - reactjs

I have a problem in my axios api calls, i'm using 2 apis in the same function, both of axios calls works good alone but when i combine them i got error in the Country components which i will be posting after, the error is ( map is not a function )
const onSerializeData = () => {
const serializedCountries = serializeCountries(selectedCountries, false);
//Print the Countries
console.log(
`Selected Countries: \n ${JSON.stringify(serializedCountries)}`
);
axios
.get(
`https://restcountries.eu/rest/v2/alpha/${
serializedCountries[serializedCountries.length - 1]
}`
)
.then((responose) => {
const CouAllData = responose.data;
console.log("CouAllData", CouAllData);
setCountryAllData([...CountryAllData, CouAllData]);
})
.catch((error) => {
console.log("Error", error);
});
axios
.get(
`https://corona-api.com/countries/${
serializedCountries[serializedCountries.length - 1]
}`
)
.then((responose) => {
const CouData = responose.data.data; //The Data
setCountryData([...CountryData, CouData]); //set The Data to the OBJ
console.log(" setCountryData", CountryData);
})
.catch((error) => {
console.log("Error", error);
});
};
return (
<div>
<Country countries={CountryData} />
<Country countriesAll={CountryAllData} /></div>
}
Country Component
const DataGroup = this.props.countries.map((county) => {
return <Data info={county} />;
});
const DataGroupAll = this.props.countriesAll.map((country) => {
return <Data infos={country} />;
});
return (
<div>
{DataGroup}
{DataGroupAll}
</div>
Data Component
<h1>Name : {this.props.info.code}</h1>
<h1>Date : {this.props.info.updated_at}</h1>
<h1>Name : {this.props.infos.name}</h1>
<h1>Capital : {this.props.infos.capital}</h1>

Try using axios.all() to perform concurrent requests.
function getCountries() {
return axios.get(
`https://restcountries.eu/rest/v2/alpha/${
serializedCountries[serializedCountries.length - 1]
}`
)
}
function getCountries2() {
return axios.get(
`https://corona-api.com/countries/${
serializedCountries[serializedCountries.length - 1]
}`
)
}
Promise.all([getCountries(), getCountries2()])
.then(function (results) {
const countries = results[0];
const countries2 = results[1];
});

Related

reactjs search bar with params endpoint

here I have a problem when creating a search bar in reactjs.
// So, I have an endpoint like this
export const getCountPoiCategoryProvinsi = (provinsi) => {
return new Promise((resolve, reject) => {
axios
.get(
`${baseUrl}/api/dashboard/v1/getCountPoiCategoryProvinsi?provinsi=${provinsi}`,
{
headers: { Authorization: `Bearer ${token}` },
}
)
.then((response) => {
resolve(response.data.data);
})
.catch((error) => {
if (error.response?.data.code === 404)
resolve({ lists: [], totalCount: 0 });
console.log(error.response);
reject(error?.response?.data?.message || "Network error.");
});
});
};
// The code for the fetch is like this
const loadPosts = async (provinsi) => {
try {
setLoading(true);
const result = await getCountPoiCategoryProvinsi(provinsi);
setPosts(result);
console.log(result);
} catch (error) {
console.log("salah");
} finally {
setLoading(false);
}
};
loadPosts();
// and the code in the return section is like this
{loading ? (
<h4>Loading ...</h4>
) : (
posts
// eslint-disable-next-line array-callback-return
.filter((value) => {
if (searchTitle === "") {
return value;
} else if (
value.title.toLowerCase().includes(searchTitle.toLowerCase())
) {
return value;
}
})
.map((item, index) => (
<h5 key={index}>
{item.category} + {item.jumlah_category}
</h5>
))
)}
When I try in the browser and type in the search bar the data doesn't appear.
the console doesn't appear either.
what do you think is wrong in my code? Thank You
According to your fetch code, your loadPosts function did not have any input when called so likely your getCountPoiCategoryProvinsi function return an empty array.

getting undefined is not a function when trying to .map an api in react

im trying to make a call to an api everything works fine until I try to map over it to render elements on the screen. im using thenewsapi.com I keep getting TypeError: undefined is not a function (near '...newStory.map...').
export default function News() {
const [newStory, setNewStory] = React.useState([]);
React.useEffect(() => {
const fetchArticle = async () =>{
const requestOptions = {
method: 'GET'
};
const params = {
api_token: 'api_key',
categories: 'business,tech,sports',
language: "en",
limit: '1'
};
const esc = encodeURIComponent;
const query = Object.keys(params)
.map(function(k) {return esc(k) + '=' + esc(params[k]);})
.join('&');
await fetch("https://api.thenewsapi.com/v1/news/all?" + query, requestOptions)
.then(response => response.text())
.then(result => setNewStory(result))
.catch(error => console.log('error', error));
}
fetchArticle()
}, [])
console.log(newStory);
const newArticle = newStory.map(({ title}, index) => {
return <h5 key={index}>{title}</h5>
})
return (
<>
{newArticle}
</>
)
}```
Its because newStory is an empty array at first and it takes time to fetch data from api you can use an if for your render like this:
return (
<>
{newStory && newStory.map(({ title}, index) => {
return <h5 key={index}>{title}</h5>
})}
</>
)
or define an empty title for your useState like this:
const [newStory, setNewStory] = React.useState([{title: ""}]);

react error unknown : TypeError: Cannot read properties of undefined

i am getting data from api (i know api is working) and i am getting error (error is after my code)
i think error is because that in first place that code runs there is no "product.data.attributes.name" and that error create please help me!
const ProductDetails = () => {
const { params } = useRouteMatch();
const productCtx = useContext(ProductContext);
const [product,setProduct] = useState({});
const getProduct = useCallback(async () => {
try {
productCtx.toggleIsLoading()
const response = await fetch(
`http://localhost:1337/api/products/${params.id}?populate=*`,
{
method: "GET",
headers : {
'content-type' : 'application/json'
}
})
if (!response.ok) {
console.log(response)
throw new Error('Something went wrong!');
}
const data = await response.json();
productCtx.toggleIsLoading()
console.log(data.data.attributes.name);
setProduct(data)
}catch (error){
console.log(error)
}
},[])
useEffect(() => {
getProduct()
},[getProduct])
useEffect(() => {
console.log(product.data.attributes.name);
}, [product]);
return(
<Fragment>
<section className="single-product">
<img src={product.img} alt={product.img} className="single-product-image"/>
<article>
<h1>{product.data.attributes.name}</h1>
<h2>${product.data.attributes.price}</h2>
<p>{product.data.attributes.description}</p>
</article>
</section>
</Fragment>
);
}
this is the error
this is the error message
You can make render condition.
const ProductDetails = () => {
const { params } = useRouteMatch();
const productCtx = useContext(ProductContext);
const [product,setProduct] = useState({});
const getProduct = useCallback(async () => {
try {
productCtx.toggleIsLoading()
const response = await fetch(`http://localhost:1337/api/products/${params.id}?populate=*`,
{
method: "GET",
headers : {
'content-type' : 'application/json'
}
})
if (!response.ok) {
console.log(response)
throw new Error('Something went wrong!');
}
const data = await response.json();
productCtx.toggleIsLoading()
console.log(data.data.attributes.name);
setProduct(data)
} catch (error){
console.log(error)
}
},[])
useEffect(() => {
getProduct()
},[getProduct])
useEffect(() => {
console.log(product.data.attributes.name);
}, [product]);
return(
<Fragment>
<section className="single-product">
<img src={product.img} alt={product.img} className="single-product-image"/>
<article>
<h1>{product.data?.attributes?.name || ''}</h1>
<h2>${product.data?.attributes?.price || ''}</h2>
<p>{product.data?.attributes?.description || ''}</p>
</article>
</section>
</Fragment>
);
}
Or You can make loading before product.data is available.
if(!product.data) return <div>Loading</div>
return(
<Fragment>
<section className="single-product">
<img src={product.img} alt={product.img} className="single-product-image"/>
<article>
<h1>{product.data?.attributes?.name || ''}</h1>
<h2>${product.data?.attributes?.price || ''}</h2>
<p>{product.data?.attributes?.description || ''}</p>
</article>
</section>
</Fragment>
);
}

How to fetch api via looped callbacks with React functional components

So I have a 40+ loop that's calling another component to display images. Each image has an ID and with that ID I can get more information about the image like Name and description via another API call.
When DisplayImage gets called I want it to call another callback function that will send out API calls for that image's metadata, store it in a variable and display it as an H1 tag.
return (
<div>
{array.map(index) => {
// Some Other Code That return a TokenID //
<>
{displayImage(tokenId)}
</>
</div>
})
const displayImage = (tokenId) => {
const imageName = GetURI(tokenId)
return (
<div className="token-container">
<h1>{imageName}</h1>
<img className="artwork" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
}
const GetURI = async (tokenId) => {
const res = await fetch("https://api"+tokenId , {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
}).then(data => {
console.log(data)
return data.json();
})
.then(data => {
return (data.name || [])
})
.catch(err => {
console.log(err);
});
}
The data is being displayed on the console but now I'm running into an infinite loop issue that I know UseEffect can solve but I can't quite figure it out. I managed to display the data on the console with UseEffect using the [] attribute but don't know how to display the data. Any help would be amazing. Thank you!
Two things useful to your situation
functions declared outside the component aren't recreated each render
useState and useEffect pairing limits calls to API to only when tokenId changes
// Put this function outside the component
// so it does not need a useCallback
// i.e not reconstructed each render of DisplayImage
const GetURI = async (tokenId) => {
...
});
const DisplayImage = (tokenId) => {
const [imageName, setImageName] = useState()
// limit calls to API to when tokenId changes
// and if eslint complains add GetURI to dependency list
// - but GetURI never changes, so no un-needed calls from it
useEffect(() => {
setImageName(GetURI(tokenId))
}, [tokenId, GetURI])
return (
<div className="token-container">
<h2>{imageName}</h2>
<img className="artwork" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
};
You can also abstract to custom hook useImageName()
const GetURI = async (tokenId) => {
...
});
const useImageName = (tokenId) => {
const [imageName, setImageName] = useState()
useEffect(() => {
setImageName(GetURI(tokenId))
}, [tokenId, GetURI])
return imageName
})
const DisplayImage = (tokenId) => {
const imageName = useImageName(tokenId)
return (
<div className="token-container">
<h2>{imageName}</h2>
<img className="artwork" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
};
BTW in GetURI this
return (data.name || [])
looks like should be
return data.name || ''
Is a different approach ok? I'd put display image into its own component.
const DisplayImage = ({tokenId: {_tokenId}}) => {
const imageName = GetURI(_tokenId)
const GetURI = useCallback(async () => {
await fetch("https://api"+tokenId , {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
}).then(data => {
console.log(data)
return data.json();
})
.then(data => {
return (data.name || [])
})
.catch(err => {
console.log(err);
});
})
});
useEffect(() => {
if (_tokenId) GetURI();
}, [GetURI]);
return (
<div className="token-container">
<h2>{imageName}</h2>
<img className="artwork" width="250px" src={`https://ipfs-asdf/${_tokenId}`} />
</div>
)
};
and then
return (
<div>
{array.map(index) => {
//Some Other Code//
<DisplayImage tokenId={tokenId} />
</div>
})
You should probably cache the response from GetURI(tokenId). No need to ask twice for the same URI when using the same tokenId.
An easy way is using react-query:
Setup in App.js:
// App.js
import { QueryClient, QueryClientProvider } from 'react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
Then use in a DisplayImage component (instead of inline function):
// DisplayImage.js
import { useQuery } from 'react-query'
export function DisplayImage(tokenId) {
const { isLoading, error, data: imageName } = useQuery(['images', tokenId], GetURI(tokenId))
return (
<div className="token-container">
<h1>{isLoading ? 'loading...' : imageName}</h1>
<img className="artwork" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
}
I found the best way to go about it with everyones help on here so thanks!
I put the GetURI function inside the show image component, and had a useEffect method call GetURI every time there was a new token ID, then I set a state variable to whatever was returned.
No loops, no errors 👌
const DisplayImage = (data) => {
const [nftMetadata, setNftMetadata] = useState();
const GetURI = async (data) => {
const nftURI = await data.drizzle.contracts.Contract.methods.tokenURI(data.tokenId).call()
await fetch(nftURI , {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
"Access-Control-Allow-Origin": "*"
},
})
.then(data => {
return data.json();
})
.then(data => {
return setNftMetadata(data || []);
})
.catch(err => {
return console.log(err);
});
});
useEffect(() => {
GetURI(data);
}, [data.tokenId])
return (
<div className="token-container">
<h2>{nftMetadata.name}</h2>
<img className="artwork" width="450px" src={`https://ipfs:/whatever/${nftMetadata.image}`} />
</div>
);
};
return (
<div>
{array.map(index) => {
// Some Other Code That returns a TokenID //
<>
<DisplayImage address={drizzle.contractList[0].address} tokenId={tokenId} drizzle={drizzle} drizzleState={drizzleState}/>
</>
</div>
})

How to fetch data from MongoDB?

I am trying to use Express + MongoDB building React app.
I was able to post some documents to MongoDB. Currently, I'm trying to figure out how to print fetched data to the screen.
I have these routes:
router.post('/totalbalance', (request, response) => {
const totalBalance = new TotalBalanceModelTemplate({
totalBalance:request.body.totalBalance,
});
totalBalance.save()
.then(data => {
response.json(data);
})
.catch(error => {
response.json(error);
});
});
router.get('/totalbalance', (request, response) => {
TotalBalanceModelTemplate.find(request.body.totalBalance, (error, data) => {
if (error) {
return error
} else {
response.json(data[0])
}
})
});
This is axios request:
useEffect(() => {
const resp = axios.get('http://localhost:4000/app/totalbalance');
console.log(resp);
}, []);
It returns a promise that has a parameter data which equals to object value which is the first value in the array
data: {_
id: "60c48b4ec60919553d92319f",
totalBalance: 5555,
__v: 0
}
and prints it out to the console.
How can I print out to the console the value totalBalance instead of whole promise?
By the way, sometime the array of data is empty (there are no documents in the DB), how should i handle these cases as well?
Thanks!
First of all, Axios GET method does not have any request body. But you are trying to use it in the MongoDB query. - "TotalBalanceModelTemplate.find(request.body.totalBalance, (error, data) => {".
The find query should be object {}. If require pass on conditions to it.
First point, to print only "totalBalance" output. Use, console.log(resp.totalBalance);
Second point, to handle records length, have a if else condition,
if (error) {
return error
} else if (data.length) {
return response.send("No records found")
} else {
response.json(data[0])
}
Try this :
Routes
router.post("/totalbalance", async (req, res) => {
try {
const totalBalance = new TotalBalanceModelTemplate({
totalBalance: req.body.totalBalance,
})
await totalBalance.save();
res.json(totalBalance)
} catch (error) {
res.status(400).json({
message: error.message
})
}
})
router.get("/totalbalance", async (req, res) => {
try {
const totalBalances = await TotalBalanceModelTemplate.find();
res.json(totalBalances)
} catch (error) {
res.status(400).json({
message: error.message
})
}
})
App.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export default function App() {
const [data, setData] = useState([]);
const getData = async () => {
try {
const response = await axios.get('http://localhost:4000/app/totalbalance');
await setData(response);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getData();
}, []);
return (
<div>
{data <= 0 ? (
<div className="empty">
<p>No data!</p>
</div>
) : (
data.map((d) => (
<ul key={d.id}>
<li>{d.totalBalance}</li>
</ul>
))
)}
</div>
);
}

Resources