Reputation: 607
I use useAxios
custom hook to avoid the code duplicity as the API I am calling is always the same.
import { useState, useEffect } from 'react';
import rest from './rest'; // inside there is a definition of axios.create baseURL...
const useAxios = (endpoint,queryString) => {
const [axiosData, setAxiosData] = useState(null);
const [axiosLoading, setAxiosLoading] = useState(true);
useEffect(() => {
const getRest = async () => {
setAxiosLoading(true);
try{
const sensors = await rest
.get('/'+ endpoint + '?' + queryString)
.then(res => {
setAxiosData(res.data);
setAxiosLoading(false);
});
}catch (e) {
console.log(e);
}
}
getRest();
}, [endpoint,queryString])
return {axiosData, axiosLoading};
}
export default useAxios;
Here is a part of the code where I call useAxios
hook.
const {axiosData:data1} = useAxios('request1','');
const {axiosData:data2} = useAxios('request2','querystring1');
const {axiosData:data3} = useAxios('request3','querystring2');
The problem I am trying to solve is how to wait for all the requests to finish, so it is for sure that all the data are ready.
I know about the axios.all
and would like to use something like that, but with usage of the reusable code like with useAxios
.
Side note: there are many useAxios calls through the app, not just the one case I explain here. That is the reason I am trying to reuse it instead using one axios.all
EDIT:
I might have mentioned that earlier, but there is also another useAxios (data4)
call in subcomponent which uses data from data1, data2, data3
and should render another subcomponent as many times as the number of objects contained in data1
. I am passing the data through Context API and need to make sure the data1, data2, data3
are available before useAxios (data4)
call
Upvotes: 1
Views: 1609
Reputation: 7717
Obviously you could do something as simple as
if (!data1 && !data2 && !data3) {
// do something
}
Since that code will run any time one of those stat variables changes. Or you could put them all as dependencies on a useEffect().
I suspect folks might steer you toward using a state machine if things are getting complicated. See useReducer()
Update based on new information You're probably aware, hooks must be called in the same order every time. You wrote this question because you're dealing with a code smell and I suspect the most 'correct' answer I can give is to think about how you can refactor the code. Break things up into smaller units and make each unit be responsible for less.
The 'quick bandaid' is to update useAxios() so you can no-op it:
const useAxios = (endpoint,queryString,bornReady=true) => {
...
if (bornReady) {
getRest();
}
...
By defaulting the new parameter to true, old code will sail on by, but you can use the flag to prevent the hook from running. Here I use the querystring as the flag as well. When you can build the querystring, you're ready to make the rest call. Before that, do nothing.
const buildQuery = (data, moreData, soMuchData) => {
if (!data || !moreData || !soMuchData) {
return undefined // no queryString for you
}
// build and return your query
return `c=${data.custId}&p=${moreData.numThings}`
}
const {axiosData:data1} = useAxios('request1','');
const {axiosData:data2} = useAxios('request2','querystring1');
const {axiosData:data3} = useAxios('request3','querystring2');
const querystring3 = buildQuery(data1, data2, data3)
const {axiosData:data4, axiosLoading} = useAxios('request4',querystring3, !!querystring3);
Do a refactor if you can. Maybe even move those calls to the server so you can make 1 request that returns only the data you need. If you're in a jam, add to the mess as illustrated above.
Upvotes: 1
Reputation: 33701
Don't destructure the axiosData
property from each object returned by the hook. That way, you can inspect the axiosLoading
property on every dataN
value to make a determination of whether or not they're all done, like this:
const data1 = useAxios('request1','');
const data2 = useAxios('request2','querystring1');
const data3 = useAxios('request3','querystring2');
const done = [data1, data2, data3].every(({axiosLoading}) => !axiosLoading);
if (done) {
// they're all done loading
}
else {
// at least one is still loading
}
Upvotes: 1