Home > Software design >  React useEffect with async/await issue
React useEffect with async/await issue

Time:11-14

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

CodePudding user response:

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 </>

CodePudding user response:

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]);

CodePudding user response:

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>
  • Related