Search code examples
typescriptreact-nativereact-hooksuse-effect

Accessing the updated state in useEffect doesn't work


I need to fetch two API-endpoints in the useEffectcall, where the second call uses the result of the first call.

So currently I have:

const [steamID, setSteamID] = React.useState<string>();
const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    setLoading(true);
    getLoggedInUser()
      .then((userObj) => userObj?.steamID)
      .then((fetchedSteamId) => {
        setSteamID(fetchedSteamId);
        console.log("steamID1: " + steamID);
        console.log("steamID2: " + fetchedSteamId);
      }).then(
         // second API-Call which needs steamID
         // by either using steamID(state) or fetchedSteamId(result)
      )
      .catch((err) => {
        setLoading(true);
      });
}, []);

I tried to append another .then() before the .catch() where I access the steamID state. But already with the code provided I have the following output:

steamID: undefined
steamID: 1234567891011

So when updating my state steamID inside useEffect, it will be undefined when accessed within useEffect unless i make any changes with hot-reload. The second output will always be correct.

I sure could just always pass the result further down, but is this the proper way? And how can I make sure, after useEffect, my state contains the correct values?


Solution

  • You could always just pass fetchedSteamId to the next then in the promise chain and use that in whatever way you need:

    const [steamID, setSteamID] = React.useState<string>();
    const [loading, setLoading] = React.useState(true);
    
      React.useEffect(() => {
        setLoading(true);
        getLoggedInUser()
          .then((userObj) => userObj?.steamID)
          .then((fetchedSteamId) => {
            setSteamID(fetchedSteamId);
            console.log("steamID1: " + steamID);
            console.log("steamID2: " + fetchedSteamId);
            return fetchedSteamId;
          }).then((fetchedSteamId) => {
            // use the id
            setLoading(false);
          })
          .catch((err) => {
            setLoading(false);
          });
    }, []);
    

    If the goal is to both set that id and use that id in another operation, it could work. Otherwise you can use another useEffect, with steamId as the dependency:

    useEffect(() => {
        setLoading(true);
        getLoggedInUser()
          .then((userObj) => userObj?.steamID)
          .then((fetchedSteamId) => {
            setSteamID(fetchedSteamId);
            setLoading(false);
          }).catch(err => setLoading(false));
    }, []);
    
    useEffect(() => {
      // do some operation when steamId useState value changes
    }, [steamId]);