Coding hobbyist, recently went through a react basics course and now studying apis. In trying to work with both I've ran into a situation where the video course instructor says fetching the api would be best to be done in the useEffect since it's reaching outside the app and could have side effects. In trying to make these calls I have one api that could get away without a clean up return function - though I'd like to write one if that is best practice the second api certainly needs a clean up but I can't map over an array of objects to fetch from a key/property in each object. What returns as data looks like an object but what sets to state is an array of promises (do not have much experience with new Promise or async/await to write these correctly to get the data returned correctly, if this is part of the solution.
Things I'm especially confused about
- First, the first useEffect clean up function was suppose to set the count state to 1, but when the component mounts, dismounts, and mounts again it still seems to enter the if block
- Second, the second useEffect if you console log the fetch at the .then block with data you get the data object but finalCoinArr is just an array of Promises
- Third, is the second useEffect clean up function going to set the gecko.data state back to an empty array? if so is there a way not to empty that array but after the first api call saving the data there tell it not to make another api call.
Here are a couple of the resources I've read
https://www.freecodecamp.org/news/async-await-javascript-tutorial/
https://beta.reactjs.org/learn/synchronizing-with-effects
Why useEffect running twice and how to handle it well in React?
function App() {
const [unsplash, setUnsplash] = React.useState({data:{urls:{full:'', regular:''},user:{name:'',portfolio_url:''}}})
const [gecko, setGecko] = React.useState({data:[]})
const [count, setCount] = React.useState(0);
const scrimbaUrl = `https://apis.scrimba.com/unsplash/photos/random?orientation=landscape&query=nature`
const coinGeckoUrl = `https://api.coingecko.com/api/v3/coins/`
const coinArr = [{name:'bitcoin', image:{small:'https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579'}},{name:'dogecoin', image:{small:"https://assets.coingecko.com/coins/images/5/small/dogecoin.png?1547792256"}},{name:'ethereum', image:{small:"https://assets.coingecko.com/coins/images/279/small/ethereum.png?1595348880"}}, {name:'litecoin', image:{small:"https://assets.coingecko.com/coins/images/2/small/litecoin.png?1547033580"}}]
React.useEffect(()=>{
if(count === 0){
fetch(scrimbaUrl)
.then(res=>res.json())
.then(data=>{
// console.log('called fetch')
setUnsplash((prevState)=>{
return {...prevState, data:data}
})
})
.catch((err)=>{
console.log(`ScrimbaE:${err}`)
let defaultBackground = 'https://images.unsplash.com/photo-1503264116251-35a269479413?crop=entropy&cs=tinysrgb&fm=jpg&ixid=MnwxNDI0NzB8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NzExNTg5MTE&ixlib=rb-4.0.3&q=80'
let defaultName = 'Aperture Vintage'
let defaultPortfolio = 'https://creativemarket.com/PedroCS?u=PedroCS'
let defaultUnsplash = {urls:{full:defaultBackground}, user:{name:defaultName, portfolio_url:defaultPortfolio}}
setUnsplash((prevState)=>{
return {...prevState, data:defaultUnsplash}
})
})
}
return ()=>{
setCount((prevCount)=>prevCount 1)
}
},[])
React.useEffect(()=>{
if(count === 0){
let finalCoinArr = coinArr.map((ele)=>{
return fetch(`${coinGeckoUrl}${ele.name}`)
.then(res=>res.json())
.then(data=>{
return data
})
})
setGecko(()=>{
return {data:finalCoinArr}
})
console.log(finalCoinArr)
}
return ()=>{
setGecko(()=>{
return {data:[]}
})
}
},[])
// console.log(gecko)
/*Returned */
// {data: Array(4)}
// data: Array(4)
// 0: Promise {<fulfilled>: {…}}
// 1: Promise {<fulfilled>: {…}}
// 2: Promise {<fulfilled>: {…}}
// 3: Promise {<fulfilled>: {…}}
// length: 4
CodePudding user response:
Every time a component mounts, it is a specific instance of that component. When it unmounts that instance gone forever, and if you re-mount it you get a brand new instance of that component, with all fresh original values. Nothing to do with the first instance. So it makes no sense to set state in a cleanup that runs on unmount, because when that instance unmounts it, along with all its internal state and everything, is discarded
The code in your second effect isn't how you do multiple API requests. The
.map
function is for creating a new array, not for looping over things. You can use it to create an array of Promises, then you can Promise.all for executing them all and getting their results:
// Here we use .map to create an array of promises and pass them to Promise.all
Promise.all(coinArr.map(coin => fetch(`${coinGeckoUrl}${ele.name}`)))
.then(results => {
// Here 'results' will be an array of all the responses
// Do with them whatever you like, like putting them into state
})
- Again, data is not preserved between mounts. If you want to preserve data then you'll have to pull it out of the component and put it somewhere like in the web storage, or in a context.