Issue
I have two arrays in state:
const [offers, setOffers] = useState([]);
const [redemptions, setRedemptions] = useState([]);
I am updating the properties within this function below:
useEffect(() => {
getData();
}, []);
const getData = () => {
Promise.all([getVenue(), getUserData(), getOffers(), getRedemptions()])
.then(() => {
console.log("OFFERS", offers); // HERE ARE THE OFFERS: [] ??
console.log("REDEMPTIONS", redemptions); // HERE ARE THE REDEMPTIONS: [] ??
})
.catch((error) => {
console.log(error);
})
.finally(() => {
setLoading(false);
});
};
const getOffers = async () => {
const offers = await fetchOffers(id);
setOffers(offers);
};
const getRedemptions = async () => {
const redemptions = await fetchRedemptions(user.uid, id);
setRedemptions(redemptions);
};
However, after all the functions have completed I need access to offers and redemptions and my console logs are returning empty arrays, although my UI is updating with the correct values.
What am I missing here?
Solution
Notice how offers
and redemptions
are const
. The values of these don't change in the current execution of your component/function when you call the setXYZ()
functions, so the code within your .then()
only sees the original unchanged empty arrays. Their values only "change" when your function rerenders/executes again and recreates the variables for that new execution of your component/function. It's the same reason why if you have a function, foo
, the variables created inside foo()
are scoped to that execution, and won't change if you happen to call foo()
again
function foo(call) {
const bar = Math.random();
console.log(`bar for fucntion call ${call} is`, bar);
setTimeout(() => { // "fake"
console.log(`After timeout, bar in ${call} is`, bar);
}, 1000);
}
foo(1);
foo(2); // This doesn't change the value of `bar` in the first function call
You can find out more information on this in the question - The useState set method is not reflecting a change immediately
What you really should do if you want to access offers
and redemptions
in your .then()
callback is have getOffers()
and getRedemptions()
return promises which resolve to the offers
and redemptions
values respectively. You actually already have functions which do this, fetchOffers()
and fetchRedemptions()
. This way, you can remove the getOffers()
and getRedemptions()
functions and call your state setter function in your .then()
callback (note: if you don't want to call them in the .then()
callback you can keep what you have now, but return
the offers
and redemptions
values from your async functions). This has the added advantage keeping your .then()
from firing after you've rendered and using values that might no longer be relevant.
Promise.all([getVenue(), getUserData(), fetchOffers(id), fetchRedemptions(user.uid, id)])
.then(([venue, userData, offers, redemptions]) => { // destructure the resolved values from each promise
console.log("OFFERS", offers);
console.log("REDEMPTIONS", redemptions);
// My suggestion here is to perform the setting in the `.then()` so that your callback logic that uses the offers and redemptions values are the same values which the user sees via the state values:
setOffers(offers);
setRedemptions(redemptions);
})
Also, you should consider using a cleanup function as part of your useEffect()
that provides the capability to cancel outgoing requests to avoid trying to update the state of a component when it no longer exists.
Answered By - Nick Parsons
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.