0

I am very confused as why my async function does not wait before running the code inside .then

For example, cards should be an array with 102 elements. However, console.log(cards.length) shows 0. Also, the last .then gets excused before any element is put into cards.

  React.useEffect(() => {
    // if (!cards.length) {
    //https://api.pokemontcg.io/v2/cards?q=set.id:base1
    async function fetchCards() {
       const response = await fetch('https://api.pokemontcg.io/v2/cards?q=set.id:base1', {
        mode: 'cors',
        headers: {
          'X-Api-key': '22173ccf-376e-4d12-8d50-8b25df6a8e13',
        },
      })
        .then((res) => res.json())
        .then((data) => {
          console.log(data.data);
          setCards(data.data);
          console.log(cards.length);
        })
        .then(() => {
           updateDeck(getRandom(cards, 4));
        });
    }
    // }
    fetchCards()
  }, []);
ruakh
  • 175,680
  • 26
  • 273
  • 307
captaincustard
  • 321
  • 2
  • 12
  • fetchCards's main body does not return anything, so the first `.then()` already gets no data to work with. Instead of `const response = await fetch(...)` just `return fetch(...)`. In fact, why even have fetchCards? Just have `fetch(url, ...).then(response => response.json()).then(......)`, no need to declare-and-invoke, you already have a promise chain by starting with fetch. – Mike 'Pomax' Kamermans Aug 02 '21 at 04:28
  • @Mike'Pomax'Kamermans I edited the code, but it still length is 0. – captaincustard Aug 02 '21 at 04:32
  • 1
    Where is `cards` coming from? Your `then` takes an argument called `data`, not `cards`, so you're not showing all your code, and I'm not going to guess what the rest does. _If_ setCards is a state function, then yes, obviously the associated `cards` won't have updated on the next line: it will be updated at the next render call, and not before. That's how state updates work in React. – Mike 'Pomax' Kamermans Aug 02 '21 at 04:36
  • Forgive my utter ignorance. do you mean I should wrap (res.json()) like that to return it? – captaincustard Aug 02 '21 at 04:37
  • cards coming from the useState initialization const [cards, setCards] = React.useState([]); – captaincustard Aug 02 '21 at 04:37
  • https://stackoverflow.com/questions/54713510/console-log-after-setstate-doesnt-return-the-updated-state – ksav Aug 02 '21 at 04:51

1 Answers1

0
  1. You shouldn't mix up your async/awaits with then callbacks.

  2. Use a new useEffect to check for when cards changes to call your updateDeck function. setCards is an asynchronous process so the new state won't be immediately available after you set it. This is why an extra useEffect is required for when the state does change.

Here's a shortened example:

useEffect(() => {
  async function fetchCards() {
    const res = await fetch(url, params);
    const data = await res.json();
    setCards(data.data);
  }
  fetchCards();
}, []);

useEffect(() => {
  if (cards.length) updateDeck(getRandom(cards, 4));
}, [cards]);
Andy
  • 61,948
  • 13
  • 68
  • 95
  • I didn't think about that! I'll try it. However, I edited the code to remove await/async and left only .then. However, still no luck. Any ideas why? – captaincustard Aug 02 '21 at 04:34
  • `setCards` is asynchronous so the new state won't be immediately available after you set it which is why an extra `useEffect` is needed for when the state does change. – Andy Aug 02 '21 at 04:39
  • Should I also put whatever function I want to run on cards in the second useEffect? – captaincustard Aug 02 '21 at 04:41
  • 1
    @captain: FYI, I've rolled back your edit. Stack Overflow is a bit different from other forums that you may have used, in that the goal is to create question-and-answer pages that are useful to other people; so once an answer has been posted, you shouldn't edit your question to invalidate it. (I mean, if the answer was a result of misunderstanding your question, then it's fine to edit the question to clarify; but if the question is about a bug and the answer shows how to fix it, then you shouldn't edit the question to fix that bug and ask about a new issue.) – ruakh Aug 02 '21 at 04:41
  • the second useEffect gets called upon first render and throws an error because cards are empty! is there away to make it wait without a conditional? – captaincustard Aug 02 '21 at 04:44
  • @captain Yes. When the `cards` state changes the 2nd `useEffect` is called. It's just a function with a callback. – Andy Aug 02 '21 at 04:44
  • It depends on what `getRandom` does, and whether you've set an initial state for `cards`. It's probably just easier to add in a condition to check the cards length. I've updated the answer. – Andy Aug 02 '21 at 04:45