'setState inside useEffect doesnt update UI

i am trying to fetch multiple endpoints in UseEffect and depending on it's result show data on the UI (avatar, username, etc.)

Problem is, that the data received in the fetch() calls inside useEffect do in fact return the correct data but when trying to access it in the UI-Code, it will not show up.

As you can see in my code, I have the isLoading state, but I feel like the then operation doesnt really wait until the response is there.. The loading screen will be shown for 0.5 sec but when the normal screen pops up, username, avatar, etc. are empty.

So how can I make the UI wait until useEffect has completely loaded the data and then display it?

UserProfile.tsx

  React.useEffect(() => {
    AsyncStorage.getItem(LOGGED_IN_USER).then((userSteamID) =>
      fetch(USER_PROFILE + userSteamID)
        .then((response) => response.json())
        .then((json) => json.steamId)
        .then((steamId) => {
          fetch(USER_INVENTORY + steamId)
            .then((response) => response.json())
            .then((json) => {
              console.log(json);
              setProfileInfo(json);
              setSteamID(json.steamId);
              setInventoryValue(json.value);
            })
            .catch((err) => alert(err))
            .finally(() => setLoading(false));
        })
    );
  }, []);

trying to use it afterwards:


  if (loading) {
    return (
      <View
        style={{
          height: "100%",
          justifyContent: "center",
          backgroundColor: Colors.dark.background,
        }}
      >
        <ActivityIndicator
          size="large"
          style={{ backgroundColor: Colors.dark.background }}
        />
        <Text
          style={{
            color: Colors.dark.text,
            marginTop: 10,
            alignSelf: "center",
          }}
        >
          retrieving userdata...
        </Text>
      </View>
    );
  } else {
    return (
      <Text>{inventoryValue}</Text> // avatar, etc. wont work as well
)

I Navigate from a LoginScreen.tsx to it like so:

  onPress={async () => {
          let result = await startAuth();
          if (result.type == "success") {
            setLoggedInUser(result.steamID);
            console.log("ID: " + result.steamID);
            navigation.navigate("UserProfile");
          }


Solution 1:[1]

Create promise chain

React.useEffect(() => {
  AsyncStorage.getItem(LOGGED_IN_USER).then((userSteamID) =>
    fetch(USER_PROFILE + userSteamID)
      .then((response) => response.json())
      .then((json) => json.steamId)
      .then((steamId) => fetch(USER_INVENTORY + steamId))
      .then((response) => response.json())
      .then((json) => {
        setProfileInfo(json);

        // This can be avoided, already available in profileInfo
        setSteamID(json.steamId); 
        setInventoryValue(json.value);       
      })
      .catch((err) => alert(err))
      .finally(() => setLoading(false))
  );
}, []);

With async/await

React.useEffect(() => {
  const init = async () => {
    try {
      const userSteamID = await AsyncStorage.getItem(LOGGED_IN_USER);
      const { steamId } = await fetch(USER_PROFILE + userSteamID).then((r) =>
        r.json()
      );
      const json = await fetch(USER_INVENTORY + steamId).then((r) => r.json());
      setProfileInfo(json);

      // This can be avoided, already available in profileInfo
      setSteamID(json.steamId);
      setInventoryValue(json.value);
    } catch (error) {
      alert(error);
    } finally {
      setLoading(false);
    }
  };
  init();
}, []);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rahul Sharma