0

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.

enter image description here

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?

I want it to be like this enter image description here

Any help would be greatly appreciated. thank you

2
  • Add some debugging console logs, one for u at the rendering, one in the state setting call. Even try saving the new state array to a variable in setFollowings and log it. Commented Aug 1, 2022 at 18:37
  • When you have 3 followings every follower will be displayed three times right? Commented Aug 1, 2022 at 18:37

2 Answers 2

1

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);
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your clear explanation. I'm not really familiar with React.StrictMode, but the takeAway is that I should not setState in .map method, since it'll trigger re-rendering while it's doing the map method, that is why im seeing this behavior of map method, is it pretty correct?
Well, in part. You were seeing the behavior of duplicate values because of the (prev) => [...prev, theUser.data] declaration. There, you were retrieving the previous state values and adding new values (when triggering the useEffect twice, you were adding that extra two values to your state). With the current answer approach, you are setting a completely new state, so if the useEffect triggers twice we are just overriding the previous values. For a reference of React.StrictMode (not sure if this is your case) you can check this answer: stackoverflow.com/a/61897567/12222861
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.