setTeams runs before the rest of the useEffect-code - javascript

I'm fetching some data from firebase firestore in my react app. This should be presented in an ul. When useEffect runs, setTeams runs before the data is fetched from firebase. How can I run setTeams after the data from firebase has been fetched?
useEffect(() => {
const teamsList = []
firebase.auth().onAuthStateChanged((user) => {
if (user) {
firebase.firestore().collectionGroup('members').where('user', '==', user.uid).get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
firebase.firestore().collection('teams').doc(doc.data().teamId).get().then((doc) => {
teamsList.push({
name: doc.data().name,
teamID: doc.data().teamId,
})
console.log('Her')
})
})
setTeams(teamsList)
console.log('Der')
})
} else {
history.push("/logg-inn")
}
})
}, [])

I reformatted your code with async await instead of then and I think it has better readability now:
Use Promise.all if you need to set a value based on multiple promises.
useEffect(() => {
firebase.auth().onAuthStateChanged(async (user) => {
if (user) {
const querySnapShot = await firebase
.firestore()
.collectionGroup("members")
.where("user", "==", user.uid)
.get();
const promises = querySnapshot.map((doc) =>
firebase.firestore().collection("teams").doc(doc.data().teamId).get());
const teams = await Promise.all(promises);
const docs = teams.map((doc) => ({
name: doc.data().name,
teamID: doc.data().teamId,
}));
setTeams(docs);
console.log("Der");
} else {
history.push("/logg-inn");
}
});
}, []);

Related

remove the TMDB API

I am currently working with a clone of a streaming platform, it turns out that this clone has the TMDB API integrated and I want to remove it to store the objects returned by this api in a firebase database, but I am a little confused.
In my Firebase file, I have a promise that returns an array of objects and it looks like this:
export const getGamesDocument = () => {
return new Promise((resolve, reject) => {
const documents = [];
firestore
.collection("games")
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
const documentData = doc.data();
documentData.id = doc.id;
documents.push(documentData);
});
resolve(documents);
})
.catch((error) => {
reject(error);
});
});
};
So far everything is going well where I am getting confused is in this redux code since I have no knowledge of the subject:
export const fetchAdventureMoviesRequest = () => ({
type: moviesActionTypes.FETCH_ADVENTURE_MOVIES_REQUEST,
});
export const fetchAdventureMoviesSuccess = (adventureMovies, isPage) => ({
type: isPage
? moviesActionTypes.FETCH_ADVENTURE_MOVIES_SUCCESS
: moviesActionTypes.LOAD_MORE_ADVENTURE_MOVIES_SUCCESS,
payload: adventureMovies,
});
export const fetchAdventureMoviesFailure = error => ({
type: moviesActionTypes.FETCH_ADVENTURE_MOVIES_FAILURE,
payload: error,
});
export const fetchAdventureMoviesAsync = (fetchUrl, isPage) => {
return dispatch => {
dispatch(fetchAdventureMoviesRequest());
axios
.get(fetchUrl)
.then(res => {
const adventureMovies = res.data.results.map(el => ({
...el,
isFavourite: false,
}));
if (isPage) {
dispatch(fetchAdventureMoviesSuccess(adventureMovies, isPage));
} else dispatch(fetchAdventureMoviesSuccess(adventureMovies));
})
.catch(error => {
const errorMessage = error.message;
dispatch(fetchAdventureMoviesFailure(errorMessage));
});
};
};
I want to remove the array of objects that are obtained in the constant "adventureMovies" and replace it with the array of objects that I obtain in the aforementioned promise.

Read array from Firebase Document

I have an array of URLS stored within a document that i'd like to read and display as individual cards. All I'm getting is the return of the whole array, I'm not mapping it correctly and I don't know where I'm going wrong.
Currently, it's displaying "https://website1.com, https://website2.com". Where as I would like it to be individual items.
const getInternalLinks = async () => {
try {
const query = await db
.collection("internallinks")
.get()
.then((snapshot) => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setInternalLinks(tempData);
});
} catch (err) {
console.error(err);
};
};
useEffect(() => {
getInternalLinks()
},[])
return (
{internalLinks.map((doc, index) => {
<Card>
<p>{doc.urls.urls}</p>
</Card>
}))
);
Firebase Collection Structure
Try adding it directly to the state:
const [internalLinks, setInternalLinks] = useState([]);
const getInternalLinks = async () => {
try {
const query = await db
.collection("internallinks")
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
const data = doc.data();
setInternalLinks([ ...internalLinks, data ]);
});
});
} catch (err) {
console.error(err);
};
};

How to get specific data from all documents from a collection in firebase?

Platform: React Native (Expo)
So I'm trying to get two values (dotCoins and name) from my firebase db and I can't figure out how to go about it. Here's my firebase structure:
This is what I currently have in place:
// Calling function when screen loads
componentDidMount() {
this.getDotCoins();
this.getUserData();
}
// Calling function when it updates
componentDidUpdate() {
this.getDotCoins();
this.getUserData();
}
// The function
getUserData = async () => {
const querySnapshot = await getDocs(collection(db, "users"));
const tempDoc = [];
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
console.log(tempDoc);
};
Both the console.log() prints nothing, and my console remains absolutely empty. I can't find where I'm going wrong since I don't receive any errors too. (I have all packages installed correctly and all functions imported too)
You are not pushing any document data to tempDoc so it'll always be empty. Try refactoring the code as shown below:
getUserData = async () => {
const querySnapshot = await getDocs(collection(db, "users"));
const tempDoc = querySnapshot.docs.map((d) => ({
id: d.id,
...d.data()
}));
console.log(tempDoc);
};
const q = query(collection(db, "users"));
const unsubscribe = onSnapshot(q, (querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(doc.data())
})
});
});
return unsubscribe;

How to show loader during multiple api calls in a loop in React

I have two API's
First API returns list of items which I am iterating to get each item's detailed data.
Here's the code
const [loader, setLoader] = useState(false);
React.useEffect(() => {
const fetchUsers = async() => {
setLoader(true);
const users = await getUsers();
const promises = users.map(async (user) => {
let userData = await getUsersDetailedData(user.userId);
return userData
});
let finalUsers = await Promise.all(promises);
setLoader(false);
}
fetchUsers();
}, [])
I am updating loader state before the api call and after call but it is not working.
Loader state is updating these many times and loader is not displaying
logs
Try it in this way,
React.useEffect(() => {
const fetchUsers = async() => {
const users = await getUsers();
const promises = users.map(async (user) => {
let userData = await getUsersDetailedData(user.userId);
return userData
});
let finalUsers = Promise.all(promises);
return finalUsers;
}
setLoader(true);
fetchUsers().then(res=>{
setLoader(false);
});
}, [])

Why is State always empty?

I have thoroughly gone through all the asked question and none of them apply to my problem directly. I am looping through an array of user ids and matching them to get a user from my firestore db. I get the result back with no problem but when i store it in the state array and run a console log, my state array is always empty. The first console.log works and shows the results from the db.
Here's my code:
const UsersScreen = (props) => {
const [state, setState] = useState({
users: []
});
const getUserProfiles = () => {
let users = [];
//networkUsers is an array with the ids
networkUsers.forEach(userId => {
db.doc(userId).get().then((doc) => {
users.push(doc.data());
console.log('localusers', users)
}).catch((error) => {
console.log('caught error', error)
})
});
setState({ users: users });
};
useEffect(() => {
getUserProfiles();
}, []);
console.log('state', state.users)
}
Please help.
The logic that fetches the document from Firestore is asynchronous. The call to setState is synchronous though. It will always before the document has been fetched. The solution would be to fetch the documents then set the state. Here is an example:
const UsersScreen = (props) => {
const [state, setState] = useState({
users: [],
});
const getUserProfiles = () => {
Promise.all(networkUsers.map((userID) => db.doc(userId).get()))
.then((docs) => {
setState({ users: docs.map((doc) => doc.data()) });
})
.catch((err) => {
console.log("caught error", error);
});
};
useEffect(() => {
getUserProfiles();
}, []);
console.log("state", state.users);
};
The Promise.all call resolves once every user has been fetched from the Firestore (maybe you could fetch them at once though). Once we have the users we loop over them with map to extract the data of the document and set the state. Here is an alternative with async/await:
const UsersScreen = (props) => {
const [state, setState] = useState({
users: [],
});
const getUserProfiles = async () => {
try {
const docs = await Promise.all(
networkUsers.map((userID) => db.doc(userId).get())
);
setState({ users: docs.map((doc) => doc.data()) });
} catch (err) {
console.log("caught error", error);
}
};
useEffect(() => {
getUserProfiles();
}, []);
console.log("state", state.users);
};

Categories