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

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)
});

Related

useEffect fetch request is pulling data twice

What I Want:
I'm pulling data from an api, then setting the data to state. I've done this inside a useEffect hook, but when I console.log the data afterwards, it's displaying the data twice, sometimes 4 times. I'm at a loss as to why this is happening.
What I've Tried:
console.log within useEffect to see data from source
disabling react developer tools within chrome.
My Code:
// make api call, assign response to data state
const [apiData, setApiData] = useState();
useEffect(() => {
async function fetchData() {
try {
await fetch('https://restcountries.com/v3.1/all')
.then(response => response.json())
.then(data => setApiData(data));
} catch (e) {
console.error('Error fetching api data', e);
};
};
fetchData();
}, []);
console.log(apiData);
Result of console.log:
As was mentioned in the other comment this is due to effects being "double invoked" in strict-mode.
A common solution and one I believe has been suggested by the React team (although am struggling to find where I read this) is to use useRef.
// make api call, assign response to data state
const [apiData, setApiData] = useState();
const hasFetchedData = useRef(false);
useEffect(() => {
async function fetchData() {
try {
await fetch('https://restcountries.com/v3.1/all')
.then(response => response.json())
.then(data => setApiData(data));
} catch (e) {
console.error('Error fetching api data', e);
};
};
if (hasFetchedData.current === false) {
fetchData();
hasFetchedData.current = true;
}
}, []);
If you are using React version 18+, StrictMode has a behavior change. Before it wasn't running effects twice, now it does to check for bugs. ( Actually, it does mount-remount, which causes initial render effects to fire twice)
https://reactjs.org/blog/2022/03/29/react-v18.html#new-strict-mode-behaviors

Async function in useEffect doesn't execute only when i'm rereshing the page

I'm new in react! and I tried to search for a solution but unfortunately, I didn't have an answer.
I'm trying to get profile data from an async function in useEffect, the first call of the component always doesn't show the data (it shows only the interface without the data ) and also doesn't show "loaded" in the console which I use it for testing, as the code shows. however, when I refresh the page all the data is loaded successfully
here's the code :
const [profile, setProfile] = useState({});
useEffect(() => {
async function fetchData() {
try {
/*some code to get the instance and account used is the function getProfile */
const response = await instance.methods.getProfile(account[0]).call();
setProfile(response.profile);
} catch (error) {
alert(
`Failed`,
);
console.error(error);
}
}
fetchData().then(()=>{ console.log('Loaded!') });
});
what should I do ! if there is no solution !
how can refresh the component one it loaded ? or something like that
first, you need to useEffect dependencylist to only run after page render and every rerender you don't give dependencylist it will execute useEffect everytime when state update, the problem is when you first load page there is no data to show what why it's showing nothing because API need time to get data so you can show loader when API is making a call
const [profile, setProfile] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
try {
setLoading(true)
/*some code to get the instance and account used is the function getProfile */
const response = await instance.methods.getProfile(account[0]).call();
setProfile(response.profile);
} catch (error) {
console.error(error);
} finally {
setLoading(false)
}
}
fetchData();
},[]);
return loading ? <div>Loading....</div> : now show your component here

How to send a GET request with SetTimeout and get data if it is not ready yet?

On Submit click I send a POST request with some data to the server and in response I get an object with id and timeout. Then I need set timeout and when time comes send a GET request for the data. The problem is that data is not ready yet and I get undefined and my React app crashes.
I was told that timeout should be from the first request only (I mean I can't mannualy increase it or do something like this: timeout * 2, I need to use timeout from the first request only). How can I do that? I think it can be done somehow with While loop ...but I'm not smart enough to write this code. Please help
const [someData, setSomeData] = useState({}) // here comes undefined and app crashes because this object renders in DOM
const getData = async (id) => {
const response = await fetch(`$BASE_URL/${id}`)
setSomeData(response)
}
const onSubmit = async (data) => {
const { id, timeout } = await fetch(url, data)
setTimeOut(() => {
getData(id) // data is not ready and I get undefined
}, timeout) // its around 1000ms and I can't change it mannually
}
If I do this then everything works fine
const getData = async (id) => {
const response = await fetch(`$BASE_URL/${id}`)
setSomeData(response)
}
const onSubmit = async (data) => {
const { id, timeout } = await fetch(url, data)
setTimeOut(() => {
getData(id)
}, 6000) // if I manually specify timeout to 6000
}
fetch will return a promises, you could use then to getData.
const onSubmit = async (data) => {
fetch(url, data)
.then(res => return res.json())
.then(res => getData(res.id))
}

Fetching data using API get request duplicate the response result data

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

React: String automatically converted to [object promise] when called from another component

I'm developing the front-end for my spring boot application. I set up an initial call wrapped in a useEffect() React.js function:
useEffect(() => {
const getData = async () => {
try {
const { data } = await fetchContext.authAxios.get(
'/myapi/' + auth.authState.id
);
setData(data);
} catch (err) {
console.log(err);
}
};
getData();
}, [fetchContext]);
The data returned isn't comprehensive, and needs further call to retrieve other piece of information, for example this initial call return an employee id, but if I want to retrieve his name and display it I need a sub-sequential call, and here I'm experiencing tons of issues:
const getEmployeeName = async id => {
try {
const name = await fetchContext.authAxios.get(
'/employeeName/' + id
);
console.log((name["data"])); // <= Correctly display the name
return name["data"]; // return an [Object promise],
} catch (err) {
console.log(err);
}
};
I tried to wrap the return call inside a Promise.resolve() function, but didn't solve the problem. Upon reading to similar questions here on stackoverflow, most of the answers suggested to create a callback function or use the await keyword (as I've done), but unfortunately didn't solve the issue. I admit that this may not be the most elegant way to do it, as I'm still learning JS/React I'm open to suggestions on how to improve the api calls.
var output = Object.values(data).map((index) =>
<Appointment
key={index["storeID"].toString()}
// other irrelevant props
employee={name}
approved={index["approved"]}
/>);
return output;
Async functions always return promises. Any code that needs to interact with the value needs to either call .then on the promise, or be in an async function and await the promise.
In your case, you should just need to move your code into the existing useEffect, and setState when you're done. I'm assuming that the employeeID is part of the data returned by the first fetch:
const [name, setName] = useState('');
useEffect(() => {
const getData = async () => {
try {
const { data } = await fetchContext.authAxios.get(
"/myapi/" + auth.authState.id
);
setData(data);
const name = await fetchContext.authAxios.get(
'/employeeName/' + data.employeeID
);
setName(name.data);
} catch (err) {
console.log(err);
}
};
getData();
}, [fetchContext]);
// ...
var output = Object.values(appointmentsData).map((index) =>
<Appointment
key={index["storeID"].toString()}
// other irrelevant props
employee={name}
approved={index["approved"]}
/>);
return output;
Note that the above code will do a rerender once it has the data (but no name), and another later when you have the name. If you want to wait until both fetches are complete, simply move the setData(data) down next to the setName

Categories