Fetching data using API get request duplicate the response result data - javascript

I am doing get request to backend to fetch data from database,
Im doing something like :
const loadData = async () => {
const response = await fetch(URL);
const data = await response.json();
setOrdersData(data.data);
};
useEffect(() => {
loadData();
console.log(OrdersData)
}, []);
when i console.log(OrdersData) it console.log 6 times thus in rendering the data it rendering it 6 times as well, i also tried to set dependency in useEffect like as follow:
const loadData = async () => {
const response = await fetch(URL);
const data = await response.json();
setOrdersData(data.data);
};
useEffect(() => {
loadData();
setLoading(false)
console.log(OrdersData)
}, [loading]);
But still when i render OrdersData it rendering it 6 times even though the response result is only one object, i couldn't figure it out how to not duplicate the data.

To prevent unnecessary renders try to use the useCallback hook in the loadData as so:
const loadData = useCallback(async () => {
try {
const response = await fetch(URL);
const data = await response.json();
setOrdersData(data.data);
} catch (err) {
//do something
}
}, [])
Remember to import as hook, and use the dependecies as you please like useEffect or useMemo.
Hoping it works remember also to unmount the the side effects in useEffect with return statement

Related

React Query useQuery Fetcher function returns undefined even though data prints

This is my second stackoverflow post regarding useQuery hooks. In this issue, i have a fetcher function that does not want to return the data. I also have an onSuccess function, however, it still returns undefined regardless of onSuccess. I was hoping to get some assistance on how useQuery should work so that the onSuccess allows me to work with the data from the fetcher function. The link to the original post from where this code derivated from is How to wait for useQuery data to finish loading to start working with it NextJS.
async function fetchDocs() {
const queryCollection = query(collection(db, loc))
const snapshot = await getDocs(queryCollection);
let arr = []
snapshot.forEach(doc => {
console.log(doc.data())
arr.push(doc.data())
})
return Promise.all(arr)
}
const {data, status} = useQuery({queryKey: ['firestoreData', db]}, {queryFn: () => fetchDocs()}, {
onSuccess: (dataCollection) => {
// do something here, not in useEffect
console.log(data)
return dataCollection
}
});

Page tries to render before data is loaded from API - React

I am pulling data from a cyrpto API that loads data of 250 coins. When I pull only 100 coins, the data is rendered fine. When I see it to 250, the data is rendered before loaded and it gives an error. The data is loaded in console when I log 250 coins.
The data function:
const fetchCoinData = async () => {
setLoading(true);
const fetchedCoinData = await getCoinsData();
setData(fetchedCoinData);
setLoading(false);
};
useEffect(() => {
fetchCoinData();
}, []);
The API call:
export const getCoinsData = async () => {
try {
const response = await Axios.get(
`https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&per_page=100&page=1&sparkline=false&price_change_percentage=1h%2C24h%2C7d`
);
return response.data;
} catch (e) {
console.log(e);
}
};
It would help if you wrapped your axios response in a promise and change your state in the then function since it seems like your state is updating before your API call is over. Something like the followng would help.
await getCoinsData()
.then(fetchedCoinData => {
setData(fetchedCoinData))
setLoading(false)
});

How to process fetched data in useEffect

When I try to proces data come from api then use it to render, but I always go to a problem with async because the process function doesn't wait for my fetching functions.
const [fetchData1, setData1] = useState([]);
const [fetchData1, setData2] = useState([]);
const [processedData, setProcessedData] = useState([]);
useEffect(() => {
const getData1 = async () => {
//get data1 using axios
//setData1(response)
}
const getData2 = async () => {
//get data2 using axios
//setData2(response)
}
getData1();
getData2();
setProcessedData(processData(fetchData1, fetchData2));
}, [])
const processData = (data1, data2) => {
//process two data
//return data;
}
Even when I try to wrap two fetching functions and the process function in an async function but the problem remains the same.
(async () => {
await getData1();
await getData2();
setProcessedData(processData(fetchData1, fetchData2));
})
Reading your question, as far as I can tell you don't need fetchData1 and fetchData2, you just want the processedData. The problem with your current code is that it's using the default values of fetchData1 and fetchData2 when calling setProcessedData, it's not using the results form axios.
Wait for both promises to settle and use their results. See comments:
const [processedData, setProcessedData] = useState([]);
useEffect(() => {
const getData1 = async () => {
//get data1 using axios
//setData1(response)
};
const getData2 = async () => {
//get data2 using axios
//setData2(response)
};
// *** Wait for both promises to be fulfilled
Promise.all(
getData1(),
getData2()
).then([data1, data2]) => { // Get those results into parameters
// *** Use the parameter values
setProcessedData(processData(data1, data2));
}).catch(error => {
// handle/report error
});
}, []);
// *** render using the current values in `processedData`
Note that since you're only do this when the component is first created, you don't need to worry about cancelling it, etc., when other state in the component changes (if it has other state). If the calls depended on other state data you were listing in the dependency array, you might need to handle disregarding earlier results if that other data changed during the calls to axios. But again, not with what you're doing here.
Promise.all is for handling multiple asnyc operations:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Here is more examples:
https://www.taniarascia.com/promise-all-with-async-await/

Return response from Axios and assign it to a variable

I'm trying to get something simple done with Axios but no luck. I want to get a response back from Axios call and assign it to a variable and do something with it next outside of the function block.
const axios = require("axios");
var posts;
(async () => {
const allPosts = await axios.get("http://jsonplaceholder.typicode.com/posts");
posts = allPosts;
})()
// do something with posts here
I know because the func is async we get to // do something with posts here first but then how we can work with the response outside of async function
Usually you would assign posts to a state variable inside the async function right after receiving the data, and that would trigger a re-render. Or you would call a redux dispatch action to set the value in redux and redux would update the props. So in short, it's the usual React way: update state or props to render the view, use componentDidUpdate for any additional logic. For example:
const axios = require("axios");
var posts;
(async () => {
const allPosts = await
axios.get("http://jsonplaceholder.typicode.com/posts");
posts = allPosts;
this.setState({
posts,
})
})()
You can consider use callback function, like below code snippet:
const axios = require("axios");
(async () => {
const allPosts = await axios.get("http://jsonplaceholder.typicode.com/posts");
someThingYouWantToDo(allPosts);
})()
const someThingYouWantToDo = (posts) => {
console.log(posts);
};
Given this is node, and not React as the original tag suggested, it could be as easy as:
const axios = require("axios");
var posts;
await (async () => {
const allPosts = await axios.get("http://jsonplaceholder.typicode.com/posts");
posts = allPosts;
})();
// do something with posts

Use function as react hook?

I wanted to use a function as a react hook to wrap fetch requests to an API.
My current hook:
export function useAPI(url, options={}) {
const [auth, setAuth] = useGlobal('auth');
const [call, setCall] = useState(undefined);
const apiFetch = async () => {
const res = await fetch(url, {
...options,
});
if (!res.ok)
throw await res.json();
return await res.json();
};
function fetchFunction() {
fetch(url, {
...options,
});
}
useEffect(() => {
// Only set function if undefined, to prevent setting unnecessarily
if (call === undefined) {
setCall(fetchFunction);
//setCall(apiFetch);
}
}, [auth]);
return call
}
That way, in a react function, I could do the following...
export default function LayoutDash(props) {
const fetchData = useAPI('/api/groups/mine/'); // should return a function
useEffect(() => {
fetchData(); // call API on mount
}, []);
render(...stuff);
}
But it seems react isn't able to use functions in hooks like that. If I set call to fetchFunction, it returns undefined. If I set it to apiFetch, it executes and returns a promise instead of a function that I can call when I want to in the other component.
I initially went for react hooks because I can't use useGlobal outside react components/hooks. And I would need to have access to the reactn global variable auth to check if the access token is expired.
So what would be the best way to go about this? The end goal is being able to pass (url, options) to a function that will be a wrapper to a fetch request. (It checks if auth.access is expired, and if so, obtains a new access token first, then does the api call, otherwise it just does the API call). If there's another way I should go about this other than react hooks, I'd like to know.
Instead of putting your function into useState, consider using useCallback. Your code would look something like this:
export function useAPI(url, options={}) {
const [auth, setAuth] = useGlobal('auth');
function fetchFunction() {
fetch(url, {
...options,
});
}
const call = useCallback(fetchFunction, [auth]);
const apiFetch = async () => {
const res = await fetch(url, {
...options,
});
if (!res.ok)
throw await res.json();
return await res.json();
};
return call
}
The returned function is recreated whenever auth changes, therefore somewhat mimicking what you tried to do with useEffect

Categories