Some Background
I am currently using React's context API to pass data collected from my API (Nodejs, Express, MongoDB) through components. There is an array named boards, in which I store the ID of various 'boards' the user is subscribed to, which will be later used to make GET or fetch requests to retrieve information about them.
When I first read the value in React:
fetch('http://localhost:8080/api/users/login', options)
.then(res => res.json())
.then( res => {
if(res.status !== 200) { setMessage(res.message) }
else {
setMessage("");
setLogged(true);
setuserID(res.userID);
setToken(res.token);
setBoards(res.boards); } //Retrieve boards here
console.log(res);
});
I later access the value of boards in a different component, where I need to use its elements to generate URLs for GET requests.
The Problem
When I log the value of boards:
useEffect(() => {
console.log(boards);
});
The value of the array in console
Though, when I try access it basically:
useEffect(() => {
console.log(boards[0]);
});
I get:
TypeError: boards is undefined
Any help on identifying the problem here would be appreciated.
Since the api needs to resolve you need to have check, you can try with below code
useEffect(() => { if(boards) console.log(boards[0]);}, [boards]);
Note:
useEffect(() => {
console.log(boards);
});
this will call every time, every render thats the reason even though the api takes time you were able to see.
You can read more about second argument of useEffect here
Related
This question already has an answer here:
react useEffect hooks with axios cannot read property of undefined
(1 answer)
Closed 4 months ago.
I am trying to fetch data but its always undefined once it gets to my return.. which makes sense because I am no longer using .then at that point. React doesnt seem to let me make the function Async so I cant really use await either? I tried setting it with useState but its still undefined where I need to use the data.
const [myData,setData] = useState('')
useEffect(() => {
fetch('https://recipesapi2.p.rapidapi.com/recipes/tomato%20soup', options)
.then((data) => data.json())
.then((data) => {
setData(data.data); console.log('this is my data', data.data)
})
.then(()=>console.log(myData))
},[])
return(
<div>{myData[0].name}</div>
) }
I would also like to note that if I remove the return, and then wait for the page to respond with the data in the log, then add the return info back, it displays the div info properly. However, if i hard refresh the browser it runs nothing, not even my console logs. So that tells me I am inputting the proper code to populate the div but its not awaiting the fetch and just throwing errors.
You have an error because myData[0] is initially an empty string, so you cannot get the name property until the data have been loaded. Just add a check for myData.
const [myData,setData] = useState('')
useEffect(() => {
fetch('https://recipesapi2.p.rapidapi.com/recipes/tomato%20soup', options)
.then((data) => data.json())
.then((data) => {
setData(data.data); console.log('this is my data', data.data)
})
.then(()=>console.log(myData))
},[])
if (!myData || !myData.length) return <div>No data</div>
return (
<div>{myData[0]?.name}</div>
)
I am attempting to perform a series of Axios requests inside the useEffect() of a react component. I am aware that these requests are asynchronous, and I should maintain a piece of "loading" state that specifies if series of requests have been completed.
const [state, updateState] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
let innerstate = []
allRespData.map(single_response => {
axios.post("<URL>", {
raw_narrative: single_response[index].response
})
.then((response) => {
innerstate.push(response.data)
});
})
updateState(innerstate)
setLoading(false)
}, []);
if (loading)
return (<h3> Loading </h3>)
else {
console.log(state)
return (<h3> Done </h3>)
}
I would expect the output from the above code to be a list containing the data of each response. Unfortunately, I think that data only arrives midway through the console.log() statement, as initially an empty list [] is logged, however the list is expandable- therein my expected content is visible.
I am having a hard time doing anything with my state at the top, because the list length is constantly 0, even if the response has already loaded (loading == false).
How can I assert that state has been updated? I assume the problem is that the loading variable only ensures that a call to the updateState() has been made, and does not ensure that the state has actually been updated immediately thereafter. How can I ensure that my state contains a list of response data so that I can continue doing operations on the response data, for example, state.forEach().
You're not awaiting any of the requests, so updateState will get called before any of the responses have had time to come back. You'll be setting the state as [] every time. You also need to return your axios.post or the data won't get passed to .then
There are lot of nicer ways to handle this (I'd recommend looking at the react-query library, for example). However, to make this work as it is, you could just use Promise.all(). Something like:
useEffect(() => {
Promise.all(
allRespData.map(single_response =>
axios
.post('<URL>', { raw_narrative: single_response[index].response })
.then(response => response.data)
.catch(error => {
// A single error occurred
console.error(error);
// you can throw the error here if you want Promise.all to fail (or just remove this catch)
})
)
)
// `then` will only be called when all promises are resolved
.then(responses => updateState(responses))
// add a `.catch` if you want to handle errors
.finally(() => setLoading(false));
}, []);
I'm currently trying to build an app in React Native. Unfortunately, I'm having a hard time understanding the state management in functional components.
The fetch resolves successfully and gives me an array of activities and I store them in the component's state. But after that I want to make more fetches regarding these activities and for that i need to know each activities' ID, so I have to access the state. This doesn't work though, as there's only an empty array printed to the console from the last log.
From the printed timestamps I can see that everything executes in the desired order and I can easily access the state in other places and get the full array of activities, but why isn't it working here?
Here's the code:
const [activities, setActivities] = useState([]);
async function getActivites(cred){
const zeroLevel = Date.now();
fetch(`https://www.strava.com/api/v3/athlete/activities?access_token=${cred.access_token}`)
.then((res) => res.json())
.then((data) => {
for (const element of data) {
setActivities(oldActivities => [... oldActivities, element])
console.log(Date.now() - zeroLevel)
}
console.log('for-loop finished', Date.now() - zeroLevel)
})
.then(() => console.log(Date.now() - zeroLevel))
.then(() => console.log(activities))
}
I already tried to store the array in another object to make it more easily accessible, but I'm almost certain there's an easier way.
If data is an array, you don't need to iterate over it, you can just set the activites with data, instead of looping over it:
.then((data) => {
setActivities(data)
console.log('fetch finished', Date.now() - zeroLevel)
return data
})
.then((data) => {
data.map(activity => // do the fetch on each activity)
}
Or if you want to base the chained fetch on the state, then you can manually observe the change like this:
.then((data) => {
setActivities(data)
console.log('fetch finished', Date.now() - zeroLevel)
})
useEffect(() => {
activities.map(activity =>// do the fetch on each activity)
},[activities])
I have two functions that run asynchronously getting data from the API. Both of them are called from their own useEffect().
I have a third function that needs to run once those two functions have been fully completed.
How can this be accomplished?
Edit:
Both of the async functions look like this:
useEffect(() => {
fetchBudgetBucketsData();
}, [fiscalYear]);
useEffect(() => {
fetchBudgetBucketsData();
}, [fiscalYear]);
const fetchBudgetsData = async () => {
setIsFetchingBudgets(true);
const res = await getBudgets(orgID, `${parseInt(fiscalYear)}`, '', budgetType);
setIsFetchingBudgets(false);
if (isErrorResponse(res)) {
console.warn(res.details);
message.error(res.displayText);
return;
}
setBudgets(res.budgets);
};
const fetchBudgetBucketsData = async () => {
setIsLoadingBudgetBuckets(true);
if (orgID === undefined) {
return;
}
const res = await getBudgetBuckets(orgID, fiscalYear);
setIsLoadingBudgetBuckets(false);
if (isErrorResponse(res)) {
console.warn(res.details);
message.error(res.displayText);
return;
}
setBudgetBuckets(res.buckets);
};
Whenever the budget data or bucket data is updated, I want to call another function that checks for errors. However when the page loads, I need it to wait for both of those functions to be finished before it checks for errors.
Edit #2:
After some debugging, it looks like the issue might have to do with when React updates the state. Since I am trying to check for errors in data saved in the state.
One way could be chaining Promises.
Promise.all([ApiCall1, ApiCall2])
// At this point two promises above will be resolved
.then(() => ApiCall3)
Read more
I discovered the issue was caused by how React chooses when to update the state and not how I was calling these functions asynchronously.
I was able to call my Error check function by hooking it into the output of the data fetch calls. This makes sure that the error check only runs when either the budgets or buckets are edited and finished being changed.
useEffect(() => {
getWarningsAndErrors();
}, [budgets, budgetBuckets]) //Update errors whenever we edit budgets or buckets
I am kinda new into react and web dev overall, I want to ask, where is the issue in my proble?
I created a database with firebase, posting into it went fine, but now I am trying to GET the date I posted before and store it Into a variable, so then I can iterate through the data and map different components for each data. I am using axios, here is the code:
function CreateBlog(props) {
const [fetchedData, setFetchedData] = useState();
useEffect(() => {
axios.get("https://diary-page-default-rtdb.firebaseio.com/diaryPages.json")
.then((response) => {
console.log(response.data);
setFetchedData(response.data);
console.log(fetchedData)
})
.catch(error => console.log("error occured:", error))
}, []);
so as I console.log the response.data I get the object with the data stored in the database, but when I setFetchData and console.log the fechedData I get undefined. Is there any simple way to store the data inside "fetchedData" as an array where every different object represents a part of the array so that later on I can map through the array and display the data in separate components?
You are storing the data correctly, but you are not able to console.log them straight away because the useState operates asynchronously.
If you want to console.log your fetchedData, have a useEffect listening to changes on that state (for demonstration purposes):
useEffect(() => {
console.log(fetchedData)
}, [fetchedData]);
A further suggestion I would give (not essential though), is to set your initial state as an empty array since that's the type of data you are storing:
const [fetchedData, setFetchedData] = useState([]);
From here, you can map through your data as such:
{fetchedData?.map((data, index) => <div key={index}>{data}</div>}
Just make sure data is not an object if you copy my example, or it will return you an error since you can't display objects inside jsx