-1

I have a problem with a simple function which where I want to call an API and then do something with the response. Basically, I just want to set my react component state to the response I get and then navigate to the other page The problem is that my code executes another part of the function before an API call is finished, and I end up on another page with console.log of undefined

There is my function:

  const startNewGame = () => {
    GameService.startGame()
      .then((response) => {
        setGame(response.data);
        console.log(game);
        navigate('intro');
      })
      .catch((e) => {
        console.log(e);
      });
  };

I can wrap my navigate into if(!game !== undefined) but then I have to click two or more times on a button.

Thank you all guys for help :)

seven
  • 166
  • 4
  • 15
  • is `setGame` asynchronous? It's not clear from your question what you're referring to as "another part of the function" that's running too soon -- `startGame` will definitely have completed (provided it's correctly returning a promise) before its `then` is executed. My guess is that `setGame` is also async but you're not waiting for its promise to resolve before calling `navigate`. – Daniel Beck Jan 06 '22 at 18:48

2 Answers2

0

Try this:

const startNewGame = () => {
  GameService.startGame()
    .then((response) => {
      setGame(response.data);
      // game is not updated yet
      // console.log(game); <- Remove this line
      // navigate('intro'); <- Remove this line
    })
    .catch((e) => {
      console.log(e);
    });
};

useEffect(()=>{
  if(game !== undefined){
    navigate('intro')
  }
}, [game])
  • The question notes that they've already tried exactly this, and that it means the button has to be clicked twice (because game is undefined on the first try, and the useEffect does nothing as a result.) – Daniel Beck Jan 06 '22 at 18:48
  • @DanielBeck, but they do it inmediately after setGame, and states are not updated inmediately – Alexander Vidaurre Arroyo Jan 06 '22 at 18:51
  • We don't know what's happening inside setGame, because it wasn't included in the question, but omitting it altogether is probably not the solution... – Daniel Beck Jan 06 '22 at 18:52
  • That's better (though now the `if game !== undefined` is unnecessary, since the useEffect will only fire when `game` changes) – Daniel Beck Jan 06 '22 at 18:58
  • @DanielBeck, this is a question about react and is probably that setGame is a function returned by useState and states update are asynchronous – Alexander Vidaurre Arroyo Jan 06 '22 at 19:01
  • @DanielBeck, if `setGame(undefined)` is called then `game` becomes `undefined` again. He should check that `game` is valid before navigate – Alexander Vidaurre Arroyo Jan 06 '22 at 19:04
  • That last point is a fair one; good call Alexander. I agree the 'setGame' is _probably_ asynchronous, based on the name, but I'm not sure it's safe to assume it as a given – Daniel Beck Jan 06 '22 at 19:31
0

You probably do several things incorrect at the same time, so to understand what exactly is wrong might take some time. Take these steps to debug your code:

  1. Make each .then call do only one thing (and stick to that principle in other cases). You could chain as many .then as you like. Moreover you could return data from one .then to the next, so your code might look like this:
 GameService.startGame()
      .then(response => response.data)
      .then(data => {
        setGame(data) 
      })
      .then(() => {
        console.log(game);
        navigate('intro');
      })
      .catch((e) => {
        console.log(e);
      });

  1. Understand your components composition. Where exactly are you saving your response? is it just local component useState or some Context Api state that wraps the app? When you navigate to "other page" your "current page" state will be unavailable to "other page" unless you keep the data somewhere up in the tree when both pages could access that.

  2. For further references keep in mind that setGame is asynchronous, so you need to wait for the state to be updated to make sure that its updated.

Vladislav Sorokin
  • 369
  • 1
  • 2
  • 14
  • Thank you mate! Lack of language knowledge... I thought that I just can put everything inside one .then block... I tried your solution, but even if it takes some time to redirect me to another page, console still printing undefined. – seven Jan 06 '22 at 19:06
  • but what exactly are you trying to console.log? what is the `game` and where does it coming from? – Vladislav Sorokin Jan 06 '22 at 19:09
  • @seven, it prints undefined because states are updated asynchronously. You must use `useEffect` to see changes – Alexander Vidaurre Arroyo Jan 06 '22 at 19:10
  • game is the response I will get from the API after sending a post request. It will create a new game object on server, with array of other objects wich will be used in app – seven Jan 06 '22 at 19:18
  • so from your words `const game = response.data`? – Vladislav Sorokin Jan 06 '22 at 19:21
  • "I thought that I just can put everything inside one .then block". For synchronous code you can; the only reason to use separate blocks is for to wait for any asynchronous steps. – Daniel Beck Jan 06 '22 at 19:30