johnoula
johnoula

Reputation: 31

Firebase data changes state but does not render component

I am trying to attatch a listener to the chatRoomIds subcollection for a specific user from the User collection.After retriving the chatroomIds for the specific users I have to loop through each to get the specific chatRoom document from the chatRoomIds array in which each element is an object containing chatRoomId that will be used to query for the specific chatRoom.The problem is that the state is working correctly but does not render the Messaage Card component unless I toggle another state in the Message component in the react dev tools on the messages link again after everything loads.

User collection with chatRoomIds subcollection

ChatRooms collection Messages component after loading

messageCard component renders after another state change happens manually or clicking the message link again after loading

function Message() {

const [currentUser] = useAuthState(auth)

const [{user}] = useStateValue()
const {show, setNotificationPopup, setShow} = useChat();
const [chats, setChats] = useState([])
const [loading, setLoading] = useState(true)
const [chatRoomArraySnap, error] = useCollection(db.collection("Users").doc(user?.uid).collection('ChatRoomIds'))
const chatList = []

const {setLoader, loader} = useLoader();
let chatRoomIds = []
let params = useParams();
const messageId = params.id;
const userObj = {
    email: user?.email,
    objId: user?.uid,

    userName: user?.displayName
};


useEffect(() => {
    {
        params.id ? setShow(true)
            :
            setShow(false)
    }

}, [])

useEffect(() =>{
    // chatList = []
    if(user.uid){
        db.collection("Users").doc(user.uid).collection('ChatRoomIds').get()
            .then(
            snapshot => {

                snapshot.docs.map((each) => {

                    // chatRoomIds.push(each.data())
                    db.collection('ChatRooms').where("chatRoomId", "==", each.data().id).orderBy('dateLastUpdated','desc').onSnapshot(snapshot => {
                        snapshot.docs.forEach(doc => {
                            chats.push(doc.data())
                            console.log(doc.data())
                        })

                        // setChats(chatList)
                        setLoading(false)





                        // snapshot.docChanges().forEach((change) => {
                        //                            if (change.type === "added") {
                        //                                console.log("New : ", change.doc.data());
                        //                                chatList.push(change.doc.data())
                        //
                        //                            }
                        //                            if (change.type === "modified") {
                        //                                console.log("Modified : ", change.doc.data());
                        //                                setNotificationPopup(change.doc.data())
                        //
                        //                            }
                        //                            if (change.type === "removed") {
                        //                                console.log("Removed : ", change.doc.data());
                        //                            }
                        //                        })


                    })

                })
                console.log(chatRoomIds)
                // chatRoomIds?.map((each) => {
                //     db.collection('ChatRooms').where("chatRoomId", "==", each.id).orderBy('dateLastUpdated','desc').onSnapshot(snapshot => {
                //         snapshot.docs.forEach(doc => {
                //             chatList.push(doc.data())
                //             console.log(doc.data())
                //         })
                //
                //         setChats(chatList)
                //         setLoading(false)
                //
                //
                //
                //
                //
                //         // snapshot.docChanges().forEach((change) => {
                //         //                            if (change.type === "added") {
                //         //                                console.log("New : ", change.doc.data());
                //         //                                chatList.push(change.doc.data())
                //         //
                //         //                            }
                //         //                            if (change.type === "modified") {
                //         //                                console.log("Modified : ", change.doc.data());
                //         //                                setNotificationPopup(change.doc.data())
                //         //
                //         //                            }
                //         //                            if (change.type === "removed") {
                //         //                                console.log("Removed : ", change.doc.data());
                //         //                            }
                //         //                        })
                //
                //
                //     })
                // })
            }

        )


        console.log(chatList)
        // if (chatRoomArraySnap) {
        //
        //     chatRoomArraySnap.docs.map((each) => {
        //         chatRoomIds.push(each.data())
        //     })
        //     // chatList=[]
        //     // setChats([])
        //
        //     chatRoomIds.map((each) => {
        //         db.collection('ChatRooms').where("chatRoomId", "==", each.id).orderBy('dateLastUpdated','desc').onSnapshot(snapshot => {
        //             snapshot.docs.forEach(doc => {
        //                 chatList.push(doc.data())
        //             })
        //
        //
        //
        //
        //
        //
        //             // snapshot.docChanges().forEach((change) => {
        //             //                            if (change.type === "added") {
        //             //                                console.log("New : ", change.doc.data());
        //             //                                chatList.push(change.doc.data())
        //             //
        //             //                            }
        //             //                            if (change.type === "modified") {
        //             //                                console.log("Modified : ", change.doc.data());
        //             //                                setNotificationPopup(change.doc.data())
        //             //
        //             //                            }
        //             //                            if (change.type === "removed") {
        //             //                                console.log("Removed : ", change.doc.data());
        //             //                            }
        //             //                        })
        //
        //
        //         })
        //     })
        //     setChats(chatList)
        //     console.log(chatList)
        //
        // }
    }

},[])

return (
    <>


        <Header/>
        <div className='container'>


            <div className='message '>
                <div className='row'>
                    <div className='col-md-4 col-lg-4 col-sm-12 lg-view'>
                        <div className=' pt-3 user-list-section'>
                            <div className='lg-view'>
                                <h5 className='text-light ml-5 mb-5`'>Messages</h5>
                                <div className={`d-flex align-items-center`}>
                                    <Search  functionHandler={handleSearchChat} props={'#13161A'}/>
                                    <CreateGroupBtn/>
                                </div>

                            </div>
                            <div className='user-list'>
                                {!loading && chats ? chats.map(chat => {
                                    // console.log(chat.data());
                                    return (<>

                                            <MessageCard  key={chat.chatRoomId}
                                                          id={chat.chatRoomId} chats={chat}/>


                                        </>
                                    )
                                }) : <></>}

                            </div>


                        </div>


                    </div>
                    {
                        !show ? <>
                                <div className='sm-view   w-100 pl-3 pr-3 '>
                                    <div className=' w-100  d-flex justify-content-center  pt-4'>
                                        <div className='flex-grow-1'>
                                            <h4 className='text-light'>Messages</h4>
                                            <p>Talk with your friends</p>


                                        </div>

                                        <div className="search-container flex-grow-1 ">
                                            <div className='search  d-flex float-right'>
                                                <CreateGroupBtn/>
                                            </div>


                                        </div>

                                    </div>
                                    <Search functionHandler={handleSearchChat}/>


                                </div>

                                <div className='col-md-4 col-lg-4 col-sm-12 sm-view'>
                                    <div className=' pt-3 user-list-section'>
                                        <div className='lg-view'>
                                            <h5 className='text-light ml-5 mb-5`'>Messages</h5>
                                            <div className={`d-flex align-items-center`}>
                                                <Search functionHandler={handleSearchChat} props={'#13161A'}/>
                                                <CreateGroupBtn/>
                                            </div>


                                        </div>
                                        <div className='user-list'>
                                            {!loading && chats !== undefined && chats !== null && chats ? chats.map(chat => {
                                                // console.log(chat.data());
                                                return (<>

                                                        <MessageCard  key={chat.chatRoomId}
                                                                     id={chat.chatRoomId} chats={chat}/>


                                                    </>
                                                )
                                            }) : <></>}
                                        </div>


                                    </div>


                                </div>

                            </>
                            :
                            <>
                                <div className='col-md-8 p-0 col-lg-8 col-sm-12'>
                                    {
                                        messageId && currentUser.email && <MessageWindow/>

                                    }

                                </div>

                            </>
                    }
                </div>
            </div>
        </div>
        {!show && <MobileNavbar/>}
    </>

);
}

Updated photo

///Working Code

function Message() {

const [currentUser] = useAuthState(auth)

const [{user}] = useStateValue()
const {show, setShow} = useChat();
const [chats, setChats] = useState([])
const [loading, setLoading] = useState(true)

let params = useParams();
const messageId = params.id;



useEffect(() => {
    {
        params.id ? setShow(true)
            :
            setShow(false)
    }

}, [])

useEffect(() => {
    if (user.uid) {
        db.collection("Users")
            .doc(user.uid)
            .collection("ChatRoomIds")
            .get()
            .then((snapshot) => {
                snapshot.docs.map((each) => {
                    db.collection("ChatRooms")
                        .where("chatRoomId", "==", each.data().id)
                        .orderBy("dateLastUpdated", "desc")
                        .onSnapshot((snapshot) => {
                            console.log(snapshot.docs.map((doc) => doc.data()))
                            setChats((chats) =>
                                chats.concat(snapshot.docs.map((doc) => doc.data()))
                            );
                            setLoading(false)
                        });
                });
            });
    }
}, []);

return (
    <>


        <Header/>
        <div className='container'>


            <div className='message '>
                <div className='row'>
                    <div className='col-md-4 col-lg-4 col-sm-12 lg-view'>
                        <div className=' pt-3 user-list-section'>
                            <div className='lg-view'>
                                <h5 className='text-light ml-5 mb-5`'>Messages</h5>
                                <div className={`d-flex align-items-center`}>
                                    <Search functionHandler={handleSearchChat} props={'#13161A'}/>
                                    <CreateGroupBtn/>
                                </div>

                            </div>
                            <div className='user-list'>
                                {/*{!loading && chats ? chats.map(chat => {*/}
                                {/*    // console.log(chat.data());*/}
                                {/*    return (<>*/}

                                {/*            <MessageCard key={chat.chatRoomId}*/}
                                {/*                         id={chat.chatRoomId} chats={chat}/>*/}


                                {/*        </>*/}
                                {/*    )*/}
                                {/*}) : <></>}*/}

                            </div>


                        </div>


                    </div>
                    {
                        !show ? <>
                                <div className='sm-view   w-100 pl-3 pr-3 '>
                                    <div className=' w-100  d-flex justify-content-center  pt-4'>
                                        <div className='flex-grow-1'>
                                            <h4 className='text-light'>Messages</h4>
                                            <p>Talk with your friends</p>


                                        </div>

                                        <div className="search-container flex-grow-1 ">
                                            <div className='search  d-flex float-right'>
                                                <CreateGroupBtn/>
                                            </div>


                                        </div>

                                    </div>
                                    <Search functionHandler={handleSearchChat}/>


                                </div>

                                <div className='col-md-4 col-lg-4 col-sm-12 sm-view'>
                                    <div className=' pt-3 user-list-section'>
                                        <div className='lg-view'>
                                            <h5 className='text-light ml-5 mb-5`'>Messages</h5>
                                            <div className={`d-flex align-items-center`}>
                                                <Search functionHandler={handleSearchChat} props={'#13161A'}/>
                                                <CreateGroupBtn/>
                                            </div>


                                        </div>
                                        <div className='user-list'>
                                            {!loading && chats ? chats.map(chat => {

                                                return (<>

                                                        <MessageCard key={chat.chatRoomId}
                                                                     id={chat.chatRoomId} chats={chat}/>


                                                    </>
                                                )
                                            }) : <></>}
                                        </div>


                                    </div>


                                </div>

                            </>
                            :
                            <>
                                <div className='col-md-8 p-0 col-lg-8 col-sm-12'>
                                    {
                                        messageId && currentUser.email && <MessageWindow/>

                                    }

                                </div>

                            </>
                    }
                </div>
            </div>
        </div>
        {!show && <MobileNavbar/>}
    </>

);
}

export default Message;

Upvotes: 1

Views: 253

Answers (1)

Drew Reese
Drew Reese

Reputation: 202618

You've a lot of commented out code, but what isn't commented out has a glaring issue, state mutation. You are pushing directly into the chats state array. This is why you have to toggle/update some other state in order to trigger the component to rerender and expose the mutation.

You should properly enqueue these chat updates. On the last/innermost snapshot you should use a functional state update to update from the previous state and because you are working in a loop, and concatenate an array of new chat documents.

Array.prototype.concat

The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

Array.prototype.map

The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.

.onSnapshot((snapshot) => {
  setChats((chats) =>
    chats.concat(snapshot.docs.map((doc) => doc.data()))
  );
});

Example:

useEffect(() => {
  if (user.uid) {
    db.collection("Users")
      .doc(user.uid)
      .collection("ChatRoomIds")
      .get()
      .then((snapshot) => {
        snapshot.docs.map((each) => {
          db.collection("ChatRooms")
            .where("chatRoomId", "==", each.data().id)
            .orderBy("dateLastUpdated", "desc")
            .onSnapshot((snapshot) => {
              setChats((chats) =>
                chats.concat(snapshot.docs.map((doc) => doc.data()))
              );
            });
        });
      });
  }
}, []);

Update - to resolve duplicates

Still use a functional state update but run the chats array through a filter first to check if there are no new chats with matching chatRoomId properties. If there is a match return false to remove it from the array, otherwise keep it. The filter function returns a new array that you can concatenate to the new chats results.

useEffect(() => {
  if (user.uid) {
    db.collection("Users")
      .doc(user.uid)
      .collection("ChatRoomIds")
      .get()
      .then((snapshot) => {
        snapshot.docs.map((each) => {
          db.collection("ChatRooms")
            .where("chatRoomId", "==", each.data().id)
            .orderBy("dateLastUpdated", "desc")
            .onSnapshot((snapshot) => {
              const newChats = snapshot.docs.map((doc) => doc.data());
              setChats((chats) => {
                const filtered = chats.filter(chat => 
                  !newChats.some(newChat => 
                    newChat.chatRoomId === chat.chatRoomId
                  )
                );
                newChats.concat(filtered);
              });
            });
        });
      });
  }
}, []);

Upvotes: 2

Related Questions