I got stuck with the following and haven't found any answer after a lot of research.
What I want to do: simply getting users inluding their images from a firestore-DB with react and the useeffect-hook and displaying them.
The DB-structure looks as follows: https://i.stack.imgur.com/sDcrv.png
So the pictures are a subcollection of the users-collection.
After getting the users from the users-collection, I'm doing a second request for adding the users images to this specific user using Object.assign. After every forEach-run over the users-collection I'm setting the users-array with setUsers((oldUsers) => [...oldUsers, currentUser]);
. Logging the users-array shows uses INCLUDING their images.
The problem: When trying to render the images, they are always undefined.
Workaround: Pressing a button that calls a function for re-setting the users:
const reRenderUsers = () => {
if (userDataLoaded === false) {
setUserDataLoaded(true);
}
const copy = [...users];
setUsers(copy);
};
^ This solves the problem and all images where shown.
Question: Is there any possibility showing the images instantly without the need of "re-rendering" the users? Am I using the useEffect-hook wrong for example? I'm thankful for any advice. Many thanks in advance!
Here the full code:
const [users, setUsers] = useState([]);
const [userDataLoaded, setUserDataLoaded] = useState(false);
useEffect(() => {
const unsubscribe = database.collection("users").onSnapshot((snapshot) => {
snapshot.forEach((doc) => {
const currentUser = {
id: doc.id,
...doc.data(),
};
database
.collection("users")
.doc(currentUser.id)
.collection("pictures")
.get()
.then((response) => {
const fetchedPictures = [];
response.forEach((document) => {
const fetchedPicture = {
id: document.id,
...document.data(),
};
fetchedPictures.push(fetchedPicture);
});
currentUser.pictures = [];
Object.assign(currentUser.pictures, fetchedPictures);
})
.catch((error) => {
console.log(error);
});
setUsers((oldUsers) => [...oldUsers, currentUser]);
});
});
return () => {
unsubscribe();
};
}, []);
const reRenderUsers = () => {
if (userDataLoaded === false) {
setUserDataLoaded(true);
}
const copy = [...users];
setUsers(copy);
};
return (
<div>
{!userDataLoaded ? (
<button onClick={reRenderUsers}> load users </button>
) : null}
{users.map((user, index) => (
<div key={user.id}>
{user.pictures && <img src={user.pictures[0].imageUrl}></img>}
</div>
))}
</div>
);
}
export default User;
This is because you are calling setUser before the firebase response completes the callback chain. You need to update the state right after the loop inside the success callback completed. I have updated useEffect to update it right after the callback
Good Luck