I was working on a little project of mine and just before deployment, the API i was using made a policy change to limit API calls to one/second. This led to me having an error(Error 429 : too many request). My work around(as axios doesn't have a retry after header prop was to create some sort of recursive function using the fetch API that makes the call again, if it fails). So, originally I ended up with the block of code below:
//recursive function to make an api call
const fetchCalls = useCallback((url, setState, retries = 4) => {
fetch(url)
.then((res) => {
// check if successful. If so, return the response transformed to json
if (res.ok) {
return res.json();
}
// else, return a call to fetchRetry if number of retries >0
if (retries > 0) {
return fetchCalls(url, setState, retries - 1);
} else {
throw new Error(res);
}
})
.then((data) => {
if (data !== undefined) {
setState(data);
Axios.all([
Axios.get(
`${api.sparklineBase}${setLowerCase(
data[0].name
)}/market_chart/range?vs_currency=${
userData.currency.code
}&from=${dayUNIX}&to=${currentUNIX}`
),
Axios.get(
`${api.sparklineBase}${setLowerCase(
data[0].name
)}/market_chart/range?vs_currency=${
userData.currency.code
}&from=${yearUNIX}&to=${currentUNIX}`
),
])
.then((ress) => {
setSparkline((prevState) => {
return [...prevState, ress[0].data];
});
setSparkline((prevState) => {
return [...prevState, ress[1].data];
});
})
.catch((errr) => {
console.log(errr);
});
}
// Do something with the response
})
.catch((error) => {
console.log(error);
});
}, [userData, setSparkline]);
useEffect(() => {
Axios.get(`${api.zoneBase}apiKey=${api.zoneKey}&include=useragent`)
.then((response) => {
setUserData(response.data);
//calling fetchCalls function below
fetchCalls( `${api.base}key=${api.key}&ids=${match.params.id}&convert=${response.data.currency.code}&interval=1d,7d,30d,365d`,
setCryptos
);
})
.catch((err) => {
console.log(err);
});
return () => {
setCryptos([]);
setSparkline([]);
};
}, [setCryptos, setSparkline, setUserData, match, fetchCalls, cryptos]);
Obviously it caused so many rerenders guessing cause some dependencies in both the useEffect and useCallback are actual async state values.
Decided to throw the fetchCall(without the useCallback) into the useEffect and i ended up with this:
//forgot to comment out this bit.
const fetchCalls = useCallback((url, setState, retries = 4) => {
fetch(url)
.then((res) => {
// check if successful. If so, return the response transformed to json
if (res.ok) {
return res.json();
}
// else, return a call to fetchRetry
if (retries > 0) {
return fetchCalls(url, setState, retries - 1);
} else {
throw new Error(res);
}
})
.then((data) => {
if (data !== undefined) {
setState(data);
}
// Do something with the response
})
.catch((error) => {
console.log(error);
});
}, []);
useEffect(() => {
Axios.get(`${api.zoneBase}apiKey=${api.zoneKey}&include=useragent`)
.then((response) => {
setUserData(response.data);
const fetchCalls = (url, setState, retries = 4) => {
fetch(url)
.then((res) => {
// check if successful. If so, return the response transformed to json
if (res.ok) {
return res.json();
}
// else, return a call to fetchRetry
if (retries > 0) {
return fetchCalls(url, setState, retries - 1);
} else {
throw new Error(res);
}
})
.then((data) => {
if (data !== undefined) {
setState(data);
Axios.all([
Axios.get(
`${api.sparklineBase}${setLowerCase(
data[0].name
)}/market_chart/range?vs_currency=${
response.data.currency.code
}&from=${dayUNIX}&to=${currentUNIX}`
),
Axios.get(
`${api.sparklineBase}${setLowerCase(
data[0].name
)}/market_chart/range?vs_currency=${
response.data.currency.code
}&from=${weekUNIX}&to=${currentUNIX}`
),
])
.then((ress) => {
setSparkline((prevState) => {
return [...prevState, ress[0].data];
});
setSparkline((prevState) => {
return [...prevState, ress[1].data];
});
})
.catch((errr) => {
console.log(errr);
});
}
// Do something with the response
})
.catch((error) => {
console.log(error);
});
};
fetchCalls(
`${api.base}key=${api.key}&ids=${match.params.id}&convert=${response.data.currency.code}&interval=1d,7d,30d,365d`,
setCryptos
);
})
.catch((err) => {
console.log(err);
});
return () => {
setCryptos([]);
setSparkline([]);
};
}, [setCryptos, setSparkline, setUserData, match, fetchCalls]);
The above code works as one would expect. I however wasn't aware of the fact that i forgot to comment out the original fetchCalls function(outside the useEffect), as i only commented out the second set of API calls that depend on the API call the fetchCalls function. Attempts to comment it out now lead to errors saying fetchCalls wasn't defined and fetchCalls being an unnecessary dependency.
Notice however that in the second block of code, the fetchCalls in the useEffect is what carries the second set of API calls that depend on the API call the fetchCalls function in the useEffect should make.
Now i just want to know why it works like this, cause it all seems weird to me. Plus is there a more acceptable workaround I could use for this?