I am making MERN social media app.
I want to show all the friends of the current user in a list in SideBar.jsx .
Home.jsx (parent of Sidebar.jsx)
import React, { Component } from "react";
import { Person } from "@material-ui/icons";
import Topbar from "../../components/topbar/Topbar";
import Sidebar from "../../components/sidebar/Sidebar";
import Feed from "../../components/feed/Feed";
import Rightbar from "../../components/rightbar/Rightbar";
import "./home.css";
export default function Home() {
return (
<>
<Topbar />
<div className="homeContainer">
<Sidebar />
<Feed />
<Rightbar />
</div>
</>
);
}
SideBar.jsx
import "./sidebar.css";
import React, { Component, useContext, useState } from "react";
...
import { axiosInstance } from "../../config";
export default function Sidebar() {
const { user } = useContext(AuthContext);
const [followings, setFollowings] = useState([]);
const followingsList = user.followings;
useEffect(() => {
const fetchFollowings = async () => {
followingsList.map(async (id) => {
try {
const theUser = await axiosInstance.get(`/users?userId=${id}`);
if (followings.includes(theUser.data)) {
} else {
setFollowings((prev) => [...prev, theUser.data]);
}
} catch (error) {
console.log(error);
}
});
};
fetchFollowings();
}, [user]);
return (
<div className="sidebar">
.....
<ul className="sidebarFriendList">
{followings.map((u) => (
<CloseFriend key={u._id} user={u} />
))}
</ul>
</div>
</div>
);
}
For example, in this case, in the state "followings", there are 2 user objects.
So, the line
followings.map((u) => (...
should only show 2 entries.
However, the result is below.
As you can see, it is showing each friend twice. I tired to check if a user object already exists in followings by doing
if (followings.includes(theUser.data)) {
} else {
setFollowings((prev) => [...prev, theUser.data]);
}
But this is not working.
How can I make sure that it only shows each user once?
Any help would be greatly appreciated. thank you
CodePudding user response:
This is happening because it seems that your useEffect
method is being fired two times (probably because you are using React.StrictMode
) and you are setting the state inside the .map
method (that is not good because you trigger a new render each time you call the setState
).
What I would recommend you to do, is to remove the setState
from the .map
method and just set the new state after you format your data. So it would be something like this:
const newFollowings = followingsList.map(async (id) => {
try {
const theUser = await axiosInstance.get(`/users?userId=${id}`);
return theUser.data;
} catch (error) {
console.log(error);
}
});
setFollowings(newFollowings);
Probably you would have to add a filtering to the array in case there are some errors (because on errors the mapped value would be undefined
):
.filter(data => data);
CodePudding user response:
When you are using the .map
function with async/await
Promise.all
usually always does the trick. Instead of pushing the state on every iteration you collect the followers list and set the state when all your fetching is done. I did not test it yet, but I hope it works.
const followingsList = user.followings;
useEffect(() => {
const fetchFollowings = async () => {
const list = await Promise.all(followingsList.map(async (id) => (
await axios.get('/user?userId=' id);
)));
setFollowers(list);
};
fetchFollowings();
}, [user]);
Note: let me know if it works, if not I'll do a little sandbox on my own