0

So I got this to work in my create-react-app but I don't quite understand why. My code is as follows:

/*
  in App.js

  Maybe not relevant but I update someParamState in another component 
*/

function App() { 
    const [ someParamState, setSomeParamState ] = useState(() => '');
    const [ someMoreState, setSomeMoreState ] = useState([]);

    useEffect(async () => {
      const result = await someRequest(someParamState);
      setSomeMoreState(result);
    }, [someParamState])

    console.log(someMoreState);
}

// in API.js
export const someRequest = async (param) => {
    let endpoint = `https://some/endpoint}`;
    const res = await axios.get(endpoint);
    return res.data;
}

If I skip the await in App.js, I will log:

Promise {<pending>}
Promise {<fulfilled>: {…}}
  __proto__: Promise
  [[PromiseState]]: "fulfilled"
  [[PromiseResult]]: Object

... and I can see I have the data in the [[PromiseResult]]: Object.

But if I keep the await in App.js, I will log the json object.

So my question is, why do I have to await the axios call twice to get the data directly? I thought I should have the data already after resolving the promise at the first await call in API.js. Also, how would this example be different with the use of .then()?

Rick
  • 199
  • 1
  • 2
  • 12
  • 2
    Sidenote: You shouldn't have your `useEffect` callback as async. You should have an async function inside the effect that you can then call. – Andy Jul 11 '21 at 11:37
  • 1
    *"So my question is, why do I have to await the axios call twice to get the data directly?"* You don't, you could remove the `async` on `someRequest` and have it return the promise from axios, use `await` once when calling `someRequest`, and use `result.data` there. But because you use `await` in `someRequest`, it has to be an `async` function, and `async` functions always return promises (because by their nature, they do asynchronous work and that's how they report completion). So you `await` the axios promise, do something with that data, and ... – T.J. Crowder Jul 11 '21 at 11:39
  • ...fulfill the promise from `someRequest` with `res.data`. Your code using `someRequest` has to use `await` because it receives a promise. (Well, it could use `.then`/`.catch`, but...) The fact you `await` twice in your current code isn't a problem. It introduces one extra async "tick" into the process but that's really, really nothing to worry about. :-) – T.J. Crowder Jul 11 '21 at 11:39
  • In addition to what @Andy said, that code also needs to check that the component hasn't been unmounted before the data arrived, before calling the state setter on a possibly-unmounted component. (Or that the response is otherwise out-of-date.) – T.J. Crowder Jul 11 '21 at 11:40
  • [Here's some reading material](https://www.robinwieruch.de/react-hooks-fetch-data). – Andy Jul 11 '21 at 11:45
  • @Rick, something the article Andy pointed you at doesn't do is cancel the request proactively, which is often better than just using a flag to check whether to update (which that article ***finally*** shows, at the end -- should be much, much earlier). [Here's an example](https://pastebin.com/F0Tfmc9Z) using `fetch` and `AbortController` (I don't use axios -- I don't see any point, we have `fetch` -- and it doesn't use the standard `AbortController`, it uses its own `CancelToken` instead). – T.J. Crowder Jul 11 '21 at 11:54

0 Answers0