Ionic react how to await other function

First of all, sorry for the bad title, i couldnt find a better one.

After i load my homepage i call a function useAuthInit().
This function sets a state Auth. I work with firebase auth and firestore for a database.
Everything works fine but i want to store a username aswell. You cannot do that with firebase authentication so i made a collection users in the firestore database. Whenever i register, the user his username and mail now gets added to a firestore collection aswell, this doc has the same id as the user his uid. This all works fine.

I want to keep the username and email in a state variable but i cant get it to work.
App.tsx calls function useAuthInit.

export function useAuthInit(): AuthInit {
    const [authInit, setAuthInit] = useState<AuthInit>({loading: true});
    useEffect(() => {
        return firebaseAuth.onAuthStateChanged((firebaseUser) => {
            //let username;
           // if(firebaseUser){
           // SOLUTION 1 via function getUserName
             //   username =  getUserName(firebaseUser.uid);
             // SOLUTION 2:
                // const fetchusername = async () => {
                //     const response =  await read(firebaseUser.uid);
                //     console.log(response);
                //     username = await response.json().username;
                // }
                // read(firebaseUser.uid)
                //     .then(result => username = result);
           // }
            const auth = firebaseUser?
                { loggedIn: true, userId: firebaseUser.uid, userName: username} :
                { loggedIn: false};
            setAuthInit({ loading: false, auth});
        });
    }, []);
    return authInit;
}

I tried multiple things. Solution 1 was to call a method that returns the username. This method looks like this atm:

async function getUserName(id) {
    let data;
    await db
        .collection("users")
        .doc(id)
        .get()
        .then((docRef) => {
            data = docRef.exists ? docRef.data() : null
        })
        .catch((error) => {
            console.log("Error getting documents: ", error);
        });

    if (data === null || data === undefined) return null
    return data.username;
}

I also tried something that looks like this:

export function getUserName(uid){
    let username;
    db.collection('users')
        .where("id", "==" , uid)
        .get()
        .then((querySnapshot) => {
            querySnapshot.forEach((doc) => {
                console.log("1", doc.data().username);
                username = doc.data().username;
            });
        })
        .catch((error) => {
            console.log("Error getting documents: ", error);
        });
    console.log(username);
    return username;
}

Or i tried to do it in the method itself (see code under SOLUTION 2)

None of this works. The closest ive come is being able to save a promise under username in the auth state.

Ive also tried to make the useAuthInit function async and await the result of the getUserName function. That case the function returns a promise and an error “property auth does not exist on type promise”. I cant find how to make it return the correct object.


const App: React.FC = () => {
    const {loading, auth} = useAuthInit();
     ....

async version:

 export async function useAuthInit() {
    const [authInit, setAuthInit] = useState<AuthInit>({loading: true});
    useEffect(() => {
        return firebaseAuth.onAuthStateChanged(async (firebaseUser) => {
            let username = await getUserName(firebaseUser.uid).toPromise();
            console.log(username);
            const auth = firebaseUser ?
                {loggedIn: true, userId: firebaseUser.uid, userName: username.body.userName} :
                {loggedIn: false};
            setAuthInit({loading: false, auth});
        });
    }, []);
    return authInit;
}

I’ve tried numerous things (and im a bit confused atm xd) but what usually happens is that the function useAuthInit gets called and executed but by the time the getUserName function returns the username, the function useAuthInit is already finished and the state is already set.

Anyone who knows how i can add the username?

ps: my explanation is a bit confusing maybe but im using the context API (with the react.createcontext function) and a custom react hook that returns this state so i can use it globally.

I don’t use Firebase auth, but it seems like the problem is simply:

  1. You have a function that returns a promise (getUserName()).
  2. You want to call that function, get the return value, and use that value in a second function.

In that case, you can use promises or you can use async/await, both are OK. But you have to use one or the other; otherwise, the second function may finish before the first.

You don’t need to make the hook async; you want to make the functions in the useEffect() return async (or Promises).

If you’re stuck, promise chains may be easier to understand than async/await.

The reason is because you do Promise1.then(() => Promise2.

So I would try writing the promise so that you return in the order of getUserName.then(() => onAuthStateChanged(). Why? Because you need the username first.