Why is State always empty? - javascript

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);
};

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);
};
};

setTeams runs before the rest of the useEffect-code

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");
}
});
}, []);

useState is not updating state immediately

here userlist is updating immediately what can be correct code for above logic
I am trying fetch userlist from firestore than traversing that list to find user details from different collection
useEffect(() => {
db.collection("following/" + Credens.uid + "/userFollowing")
.get()
.then((snapshot) => {
followingList = snapshot.docs.map((value, ind) => value.data());
})
.then(() => {
if (followingList.length > 0) {
followingList.map((value, index) => {
db.collection("Postss")
.doc(value.useruid)
.collection("MovieWatched")
.get()
.then((snaps) => {
// let Detail = snap.data()
let movieidList = snaps.docs.map(
(value) => value.data().postMovieId
);
if (movieidList.includes(MovieId) === true) {
setuserList((prev) => [...prev, value.useruid]);
}
});
});
}
})
.then(() => {
console.log(userList);
userList.map((value, index) => {
db.collection("users")
.doc(value)
.get()
.then((snapshot) => {
setfriendsWatchedData((prev) => [
...prev,
{
usersID: value,
userData: snapshot.data(),
},
]);
});
});
});
// return () => {
// cleanup
// }
}, []);
To be sure the state did change, you can use the useEffect() to monitor the changing of that state like:
useEffect(() => {
if (userList) {
// userList.map....
}
}, [userList])
Additional conditions can be specified in the if statement. The hook will run whenever the state changes.

How to fetch a json object while using hooks in reactjs

I'm trying get my head around hooks in react, seemed pretty easy until I tried using fetch to get a json Object, the code I used is below
const [row, setRow] = useState({
response: null,
error: false,
loading: true
});
useEffect(() => {
setRow({...row, error: null, loading: true});
fetch("/todo?page[number]=1&page[size]=100000")
.then(async (response) => {
const data = await response.json();
setRow({
response: data,
error: !response.ok,
loading: false,
});
console.log('response', data);
console.log('Data fetched', row);
})
.catch((err) => {
setRow({
response: {status: "network_failure"},
error: true,
loading: false,
})
console.log('err' + err);
});
}, []);
Which produces the following result:
If could give some hints I would be really be appreciated, Thanks.
Ok, you've mixed and matched synchronous and asynchronous programming in the same block, so lets simplify and just take the complete asynchronous approach using the async/await declarations. It immediately transformers your useEffect into this:
useEffect(async () => {
try {
const response = await fetch("/todo?page[number]=1&page[size]=100000");
const data = await response.json();
setRow({
response: data,
error: !response.ok,
loading: false
});
} catch (e) {
setRow({
response: { status: "network_failure" },
error: true,
loading: false
});
console.error(e);
}
}, []);
As for your original question, it was fetching and returning the JSON as you requested, see the screenshot output. If you want to render the todo list, which is what I presume you want to do, change the setState to this:
const [loading, setLoading] = useState(true);
const [todos, setTodos] = useState([]);
const [error, setError] = useState(undefined);
Then we update the useEffect...
useEffect(async () => {
try {
const response = await fetch("/todo?page[number]=1&page[size]=100000");
const data = await response.json();
setTodos(data);
setLoading(false);
setError(undefined);
} catch (e) {
setLoading(false);
setError(e.message);
setTodos([]);
}
}, []);
Then we add rendering...
const MyComponent = () => {
// ... useEffect & useState code
if (error) {
return (<p>There was an error loading todos, error: {error}</p>);
}
return (
<div>{todos.map(todo => <p key={todo.id}>{todo.title}{todo.completed ? " (completed)" : ""})}</p></div>
);
};
Working example on codepen using https://jsonplaceholder.typicode.com (it takes a while to load)
https://codepen.io/jmitchell38488/pen/GRoqeJp

Categories