I'm creating a chat feature in my application that leverages Firebase. I've read the docs about adding event listeners, but I can't seem to get the listener to update the state (I believe when the state gets updated, the app re-renders and should have the new, incoming message)
Use Case: you're having a chat with someone and you have the messaging/chat screen up and dont want to have to refresh the page to see new messages... they should display as the message gets posted to the DB
componentDidMount() {
this.setState({ isLoading: true })
this.pullMessages()
this.listener()
this.setState({ isLoading: false })
pullMessages = () => {
let messagesArray = []
if (this.state.chatroomDetails.users.length === 2) {
firebase.firestore().collection('chatrooms').doc('private').collection(this.state.chatroomDetails.key).doc('messages').collection('messages').get().then((querySnapshot) => {
if (!querySnapshot.empty) {
querySnapshot.forEach((doc) => {
messagesArray.push(doc.data())
this.setState({ messages: messagesArray })
})
}
})
}
else {
firebase.firestore().collection('chatrooms').doc('group').collection(this.state.chatroomDetails.key).doc('messages').collection('messages').get().then((querySnapshot) => {
if (!querySnapshot.empty) {
querySnapshot.forEach((doc) => {
messagesArray.push(doc.data())
// console.log(messagesArray)
this.setState({ messages: messagesArray })
})
} else {
console.log('Nothing to Grab')
}
})
}
}
listener = () => {
let messagesArray = []
if (this.state.chatroomDetails.users.length === 2) {
firebase.firestore().collection('chatrooms').doc('private').collection(this.state.chatroomDetails.key).doc('messages').collection('messages')
.onSnapshot(function (querySnapshot) {
if (!querySnapshot.empty) {
querySnapshot.forEach(function (doc) {
console.log('the console');
console.log(doc.data());
messagesArray.push(doc.data())
})
}
})
this.setState({ messages: messagesArray })
}
}
in listener(), i can see the new message that comes through from the other device, but i can't seem to set it to my state from here... help!
The state is likely not updating because you are mutating it. When this line runs:
messagesArray.push(doc.data())
this.setState({ messages: messagesArray })
You are adding an item to the messages array that is already in the state and then setting the state to the same value which won't re-render your component.
You can correct this by setting the messages value to a new array each time you receive an update on the collection. Like this:
this.setState({ messages: [...messagesArray, doc.data()] })
EDIT:
I have also noticed that your state update is not in the snapshot listener here:
.onSnapshot((querySnapshot) => {
if (!querySnapshot.empty) {
querySnapshot.forEach((doc) => {
console.log('the console');
console.log(doc.data());
messagesArray.push(doc.data())
})
}
})
this.setState({ messages: messagesArray })
It should look this like:
.onSnapshot((querySnapshot) => {
if (!querySnapshot.empty) {
querySnapshot.forEach((doc) => {
this.setState({ messages: [...messagesArray, doc.data() })
})
}
})
or even better to prevent needlessly updating the state:
.onSnapshot((querySnapshot) => {
if (!querySnapshot.empty) {
const newMessages = querySnapshot.map((doc) => doc.data())
this.setState({ messages: [...messagesArray, ...newMessages })
}
})