Updating react state when receiving an array of object - javascript

I am using an axios.get to make a call to my MongoDB. My response from the DB is an array of objects containing all the data from the database. I am only trying to save the username of each user to state. I am trying to set the response (res.data.username) to my state however when i log my state I am getting an empty array back. PS: There was no way to copy my response so i added an image of the response for reference, let me know if there's a better way to show the response
const [users, setUsers] = useState([]);
useEffect(() => {
axios.get('http://localhost:5000/users')
.then(res => {
if (res.data.length > 0) {
console.log(res.data)
setUsers(user => [...user, res.data.username]);
}
})
}, [])

Since users is an array, Pass the array to setUsers.
Use destructuring to for readability and simplification.
const [users, setUsers] = useState([]);
useEffect(() => {
axios.get("http://localhost:5000/users").then((res) => {
if (res.data.length > 0) {
console.log(res.data);
setUsers(res.data.map(({ username }) => username));
}
});
}, []);

res.data is an array.
to just set username from response you can try
setUsers(user => [...user, res.data.map(response => response.username)]);

setUsers(res.data.map(({ username }) => username));

Related

React-Native Firestore - Get user info for a comment section

I'm building an app using react-native and react-native-firebase and i'm running into an issue while trying to implement a comment section.
My tree of data is currently like that :
collection(comments)/doc(currentUser.uid)/collection(userComments)/doc(commentCreatorID)
Within this doc commentCreatorID there is all the data i need. So basically the content, a timestamp...
For this part everything works perfectly but in order to havethe commentCreator's infos stick with his post, i need to grab them somewhere else.
The way i do that is taking the doc(commentCreatorID), as it is the uid of this user and ask firestore to give me the data from the document with this same id within my "users" collection.
Here is my code :
const [comments, setComments] = useState([])
const [commentsReady, setCommentsReady] = useState([])
useEffect(() => {
setComments([])
setLoading(true)
firestore()
.collection('comments')
.doc(auth().currentUser.uid)
.collection('userComments')
.get()
.then((snapshot) => {
let comments = snapshot.docs.map(doc => {
const data = doc.data()
const id = doc.id
return {id, ...data}
})
setComments(comments)
})
.then(() => {
comments.forEach(comment => {
firestore()
.collection("users")
.doc(comment.id)
.get()
.then((snapshot) => {
const data = snapshot.data()
setCommentsReady({comments, ...data})
})
})
})
console.log(commentsReady)
setLoading(false)
}, [handleScroll4])
This doesn't seem to works well as for now. My log throw an empty array right into my face..
I'm grabbing each comment correctly tho and even each user's data corresponding to their uids.
I can log them once ForEach have been done.
But for some reason i can't have them set to my state commentsReady.
Did i miss something ?
Thanks for your time
The setters that the useState function returns are async. Copying data from one state to another is also an antipattern. Try using effects.
const TT = () => {
const [comments, setComments] = useState([]);
const [userInfos, setUserInfos] = useState([]);
const commentsView = comments.map(comment => {
// Reactively merge each comment with the appropriate user info
});
useEffect(() => {
firestore()
.collection('comments')
.doc(auth().currentUser.uid)
.collection('userComments')
.get()
.then((snapshot) => {
let comments = snapshot.docs.map(doc => {
const data = doc.data()
const id = doc.id
return {id, ...data}
});
setComments(comments);
});
}, []);
useEffect(async () => {
// Maybe keep a cache of fetched user datas, but not sure how this is architected
const snapshots = await Promise.all(comments.map(comment => {
return firestore()
.collection("users")
.doc(comment.id)
.get();
}));
setUserInfos(snapshots.map(snap => snap.data()));
}, [comments]);
};

Matching post author to post in React from state

I am learning React and I have successfully mapped the post titles to a div on the page. I also need to take the "userid" from the posts and get the user's name from the users state and add it to a on the div.
I can send the userid to a function which is set to return the corresponding user but it does not show up on the page. I would appreciate a review of my code to see where I am going wrong. I can log out to the console the right user but it doesn't show up on the HTML. The second in the section is the one that is not populating I would need to do that on comments as well later on so for now I just assigned a static innerHTML for that. here is my code that I have. Thanks in advance.
export default function Posts(name) {
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
const [users, setUsers] = useState([])
// get Posts
useEffect(() => {
getPosts()
.then(items => {
setPosts(items)
})
// Get all Users
getUsers()
.then(users => {
setUsers(users)
})
// Get all comments
getComments()
.then(comments =>{
setComments(comments)
})
}, [])
// get individual user by userId
const getPostAuthor = (userid)=> {
users.filter((item) =>{
if ( item.id === userid){
console.log(item.name)
return item.name
}
})
}
return ( <PostsSection> {
posts.map(item => <Post><PostTitle key ={ item.title }> { item.title } </PostTitle><PostBody key={item.body}>{item.body}</PostBody><PostFooter><p>comments:4</p><p>{getPostAuthor(item.userId)}</p></PostFooter></Post>)}
</PostsSection> )
}
users.filter is returning item.name, but getPostAuthor() does not return anything. You could put a return before your users.filter function but since Array.filter() returns an array and you want one user I'd expect that is not what you want. You could instead try Array.find

Function not getting all documents in a collection

I know there is a similar question that exists here but it doesn't really answer the question on here. My code currently on get the last two documents in a collection.
const [recipedata, setRecipeData] = useState([]);
const fetchRecipes = async () =>{
const response = fire.firestore().collection("recipes");
const data = await response.get();
data.docs.forEach(item=>{
setRecipeData([...recipedata, item.data()])
})
}
useEffect(() => {
fetchRecipes();
}, [])
Again when I print (recipedata) it only show two objects in the array. Can anyone tell me why this might be happening?
The query is correct and should fetch all the documents in the collection. You should check the the collection again. Also modifying the code like this should be better so you are updating the recipes array only once.
const [recipedata, setRecipeData] = useState([]);
const fetchRecipes = async () =>{
const response = fire.firestore().collection("recipes");
const data = (await response.get()).docs.map(doc => doc.data());
console.log(data)
//Updating only once
setRecipeData(data)
}
useEffect(() => {
fetchRecipes();
}, [])
Please share your collection as well so we can try replicating any issue if it has something to do with Firestore.

Reactjs : TypeError: Cannot read property 'map' of undefined

I've been working on that React Project and I've been using axios to fetch data from my backend. I received the (TypeError: Cannot read property 'map' of undefined) error multiple times and I've tried multiple fixes with no hope of fixing. I added a hook for loading to ensure that data is present before rendering, but If I go back and fourth between pages I receive the same error
Here's my code:
function Developer(props) {
const [data, setData] = useState();
const [loading , setLoading] = useState(true)
const [error, setError] = useState();
var id = props.match.params.id
var location = props.match.params.location
useEffect(() => {
axios(`http://localhost:5000/api/${location}/${id}`)
.then((response) => {
setData(response.data);
})
.catch((error) => {
console.error("Error fetching data: ", error);
setError(error);
})
.finally(() => {
setLoading(false);
});
}, []);
if (loading) return "Loading...";
if (error) return "Error!";
return (
<div>
<h1>{data.Developer}</h1>
<ul>
{
data.Projects.map((project) => (<li key = {project._id}>
<a href= {`/${location}/${id}/${project._id}`}>{project.Project}</a></li> ))
}
</ul>
</div>
)}
you should do a check on the nature of your data property before making a map on it.
{
data && data.Projects.length !== 0 &&
data.Projects.map((project) => (<li key = {project._id}>
<a href= {`/${location}/${id}/${project._id}`}>{project.Project}</a></li> ))
}
and i recommend you to use javascrip fetch
I've encountered this very error myself many times.
First of all check if the data you are getting from the api is an array of javascript objects
Here is a simple solution.
{data ? data.Projects.map((project) => (<li key={project._id}>
<a href={`/${location}/${id}/${project._id}`}>{project.Project}</a></li>)) :<div>Loading..</div>}
So what I have done here is use a ternary operator, what it will do is check if data state has data yet, if it has it will show the data other wise it will show "Loading..."
Axios takes some time to fetch and set data, but the page renders before that so thats why it is showing Cannot read property 'map' of undefined
It's possible that the data returned doesn't contain what you need.
Try to print out the returned data
axios(`http://localhost:5000/api/${location}/${id}`)
.then((response) => {
console.log(response.data);
setData(response.data);
})
it could be a valid response, but doesn't contain that variable data.Projects. If you want to handle the absence of that value, you can use something like:
(data.Projects || []).map(...)
this way, if data.Projects is falsy, it would be replaced by an empty array and it would call [].map(...).
as discussed, your setLoading(true) requirement.
function Developer(props) {
const [data, setData] = useState();
const [loading , setLoading] = useState(true)
const [error, setError] = useState();
var id = props.match.params.id
var location = props.match.params.location
useEffect(() => {
// ooh look, we are in a loading state here, cause the data hasn't come back from the server at this point.
setLoading(true);
axios(`http://localhost:5000/api/${location}/${id}`)
.then((response) => {
setData(response.data);
// awesome, data has returned from the server, we aren't loading anymore, but the finally block handles that scenario for us.
})
.catch((error) => {
console.error("Error fetching data: ", error);
setError(error);
})
.finally(() => {
setLoading(false);
});
}, []);
if (loading) return "Loading...";
if (error) return "Error!";
return (
<div>
<h1>{data.Developer}</h1>
<ul>
{
data && data.Projects.length && data.Projects.map((project) => (<li key = {project._id}>
<a href= {`/${location}/${id}/${project._id}`}>{project.Project}</a></li> ))
}
</ul>
</div>
)}

I'm getting the first response an empty list

I'm getting data from API like this:
const [software, setSoftware] = useState([]);
const id = match.params.id;
useEffect(() => {
fetch(`http://127.0.0.1:8000/api/software/${id}/`)
.then(response => response.json())
.then(data => {
setSoftware(data)
})
}, [id]);
First response is an empty list, but the next response is my list from API. I tried to use useEffect because setSoftwares is asynchronous, but it didn't help.
So how can I get only my list?
I think you are sending incorrect id for the first time, try to console.log(id) it and check-in the console if id is valid or not.

Categories

Resources