0

I am using Firebase in this component to retrieve an array of some basic user details. The array size is always 3 with this query. The function that carries out this action is whoToFollow() and when the results are received the function updates a state variable 'who' There is no array mutation happening here. I simply reset the state to the returned value. Unfortunately despite confirming that the state has indeed been updated, the component does not re-render to display this. In order to simplify the code I have only used the length of the array as the value to be displayed.

Below the code I have included a screenshot of the console logs which display the code execution steps and shows a confirmation of the state value being changed. (returned data is made up test data)

const storage = getStorage(firebaseApp);
const db = getFirestore();

function Aside() {

  const [who, setWho] = useState([])

  const whoToFollow = async () => {
    const usersRef = collection(db, "users");
    const q = query(usersRef, limit(3));
    const querySnapshot = await getDocs(q);
    const results = await querySnapshot.docs.map(doc => doc.data());
    let filteredArray = []

    results.forEach(async (user) => {
      let imgPath = await getDownloadURL(ref(storage, user.profilePicture));

      let tmpObj = {
        uid: user.UID,
        displayName: user.displayName,
        userName: user.userName,
        profilePicture: imgPath
      };

      filteredArray.push(tmpObj);
    });
    console.log('Users recieved and updating who state')
    setWho(filteredArray)
  }


  useEffect(() => {

    console.log('component did mount, calling "WhoToFollow()" function')
    whoToFollow()

  }, []);


  useEffect(() => {

    console.log('who state has been updated but nothing is being rendered?')
    console.log(who)
  
  }, [who]);
  
  return (

    <aside> 

      <div className='aside-head'>Search</div>
      <div className='aside-cont'>
        <div className='who-to-follow'>
          <div className='wtf-head'>
            New users to follow
          </div>
          <div className='wtf-list'>

            {who.length}
 
          </div>
        </div>
      </div>
    </aside>
  );
}

export default Aside;

enter image description here

Jacob Riches
  • 177
  • 8
  • 1
    When you call `setWho`, `filteredArray` is empty, because there haven't been any calls to `push` -- **yet**. Later, when you look at it in the console having logged it with `console.log` in the `useEffect` callback, during the intervening time, `push` calls *have* been made (and `console.log` shows you [the contents as of when you expand them, not as of when they're logged](http://stackoverflow.com/questions/38660832/element-children-has-elements-but-returns-empty-htmlcollection)). If you want to retrieve those things in *series* (one after another), use a `for-of` loop. If you want to ... – T.J. Crowder Aug 03 '21 at 15:56
  • ...retrieve them in *parallel* (overlapping requests), use `const filteredArray = await Promise.all(results.map(user => { const imgPath = await getDownloadURL(ref(storage, user.profilePicture)); return { uid: user.UID, displayName: user.displayName, userName: user.userName, profilePicture: imgPath }; });` and then `setWho(filteredArray);` (but just a note, it's not *filtered*, it's just modified). (Parallel seems the right thing for that code, FWIW.) – T.J. Crowder Aug 03 '21 at 15:58
  • Just to clarify (meant to say above) the reason that no `push` calls happen during the `forEach` is that `forEach` doesn't wait for an `async` function's promise to settle before going to the next entry. So the `forEach` *starts* the process for each entry, but none of them finishes before you call `setWho`. – T.J. Crowder Aug 03 '21 at 16:00
  • 1
    @T.J.Crowder why didn't you write all that in an answer? (I assume that it was you that closed the question. I'm just asking friendly to understand how should I manage this cases in the future). – Ernesto Stifano Aug 03 '21 at 16:01
  • @ErnestoStifano - On SO, we're supposed to direct questions that basically ask the same question as a previous one to the answers of that question, so we don't end up with the problem most sites like this have of a hundred different questions asking the same thing with 200 *slightly different* answers and the poor person searching having to sift through it all. So I try to do that. But at the same time, if I think there's any clarification needed, I'll do that in a comment (or three) to help the OP understand how the linked duplicate answers address their *specific* situation. ... – T.J. Crowder Aug 03 '21 at 16:24
  • ... Since comments are second-class citizens on SO and answers are first-class citizens, hopefully the comments don't cause the problem that SO is trying to avoid. But it's an imperfect system, and I'm imperfect at working within it, so results vary. :-) (Which could sound defensive, but I hope it doesn't; I took your question in the friendly way you clearly meant it and intend my reply in the same way. :-) ) – T.J. Crowder Aug 03 '21 at 16:27
  • 1
    Thanks for the help TJ! been stuck on that for while now. If only I realized the problem originated from the forEach I would have saved myself so much time! Really appreciate the help – Jacob Riches Aug 03 '21 at 17:32
  • Really glad that helped @JacobRiches! :-) Yeah, it's one of those subtle things. – T.J. Crowder Aug 04 '21 at 08:11

0 Answers0