skip to Main Content

I am trying to add some properties to an array of objects inside useEffect but it renders DOM while those fields are still not present. Is there any reliable way how to wait till those properties are added:

useEffect hook look like this:

  useEffect(() => {
    onSnapshot(query(collection(db, "conversations"), where('canRead', 'array-contains', user.user.uid), orderBy("lastMsgDate", 'desc')),
      async (snapshot) => {
        let conversations = snapshot.docs.map(doc => toConversation(doc, user.user.uid));
        await conversations.map(async (convo, index) => {
          const profile = await getDoc(doc(db, "users", convo.canRead[0]))
          conversations[index].picture = profile.data().picture
          conversations[index].name = profile.data().name
        })
        setConversations(conversations)
      })
  }, []);

This is how I am rendering list of convos:

<IonCard>
  {conversations.length > 0 ?
    conversations.map((conversation) =>
      <IonItem key={conversation.id}>
        <IonAvatar slot='start'>
          <img src={conversation.picture ? conversation.picture : '/assets/default-profile.svg'} alt={conversation.name} />
        </IonAvatar>
        <IonLabel>{conversation.name}</IonLabel>
      </IonItem>
    )
    :
    <IonCardContent>
      no convos
    </IonCardContent>
  }
</IonCard>

the name and picture does not render even i can see it when log array into console

0:{ 
  canRead: ['iwPmOBesFQV1opgs3HT9rYPF7Sj1'],
  id: "W6cefXGoBAZijPof8jVl",
  lastMsg: "test",
  lastMsgDate: nt {seconds: 1668418292, nanoseconds: 281000000},
  lastMsgSender: "Hyw4Argt8rR25mFaFo1Sl4iAWoM2",
  name: "Warren"
  picture: "https://firebasestorage.googleapis.com/v0/b/..."
  users: {
    Hyw4Argt8rR25mFaFo1Sl4iAWoM2: true,
    iwPmOBesFQV1opgs3HT9rYPF7Sj1: true
  }
}

Any help appreciated

3

Answers


  1. Chosen as BEST ANSWER

    for some reason it works with setTimeout function which is not the best solution

      useEffect(() => {
        setLoading({ loading: true, loadingMsg: 'Loading conversations' })
        onSnapshot(query(collection(db, "conversations"), where('canRead', 'array-contains', user.user.uid), orderBy("lastMsgDate", 'desc')),
          async (snapshot) => {
            let convos = snapshot.docs.map(doc => toConversation(doc, user.user.uid));
            await convos.map(async (convo, index) => {
              const profile = await getDoc(doc(db, "users", convo.canRead[0]))
              convos[index].picture = profile.data().picture
              convos[index].name = profile.data().name
            })
            setTimeout(() => {
              setConversations(convos)
              setLoading({ loading: false, loadingMsg: undefined })
            }, 1000);
          })
      }, [user.user.uid]);
    
    

  2. Maybe u can add a loading state? something like

    const [loading, setLoading] = useState(false);
      const [conversations,setConversations] = useState(null);
      const onSnapshot = async () => {
        setLoading(true)
        onSnapshot(
          query(
            collection(db, "conversations"),
            where("canRead", "array-contains", user.user.uid),
            orderBy("lastMsgDate", "desc")
          ),
          async (snapshot) => {
            let conversations = snapshot.docs.map((doc) =>
              toConversation(doc, user.user.uid)
            );
            await conversations.map(async (convo, index) => {
              const profile = await getDoc(doc(db, "users", convo.canRead[0]));
              conversations[index].picture = profile.data().picture;
              conversations[index].name = profile.data().name;
            });
            setConversations(conversations);
          }
        );
    
        setLoading(false);
      };
     
      useEffect(() => {
        onSnapshot()
      },[])
    
      return loading ? <span>loading...</span> : <div>{conversations.map((con,i) => <span key={i}>con</span>)}</div>
    
    Login or Signup to reply.
  3. You can use a loading message or a gif.

     const [loading, setLoading] = useState(true);
    
     useEffect(() => {
       onSnapshot(query(collection(db, "conversations"), where('canRead', 'array-contains', user.user.uid), orderBy("lastMsgDate", 'desc')),
          async (snapshot) => {
            ....
            setConversations(conversations);
            setLoading(false);
          })
     }, []);
    
     if(loading) return <p>Loading...</p>
    
     return <> your content </>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search