Reputation: 315
I want to automatically fetch new incoming messages from a firestore collection using onSnapshot. While I can set the state inside the callback, I cannot read it.
const [messages, setMessages] = useState(null);
const [chat, setChat] = useState(props.chatId);
useEffect(() => {
const q = query(collection(db, "messages"), where("chat_id", "==", chat), orderBy("date","desc"), limit(5));
// Create the DB listener
const unsuscribe = onSnapshot(q, (querySnapshot) => {
console.log(messages);
if(messages === null){
console.log("setting messages the first time");
setMessages(querySnapshot.docs)
}else{
console.log("updating messages");
setMessages([...querySnapshot.docs, ...messages])
}
});
return () => {
console.log("unsubscribe");
unsuscribe();
}
}, [chat]);
Whenever onSnapshot
fires, messages
is always null
but setMessages
works since the messages are displayed. I tried so many approaches but I could not get it to work.
Help much appreciated.
Upvotes: 3
Views: 8274
Reputation: 435
It shows up as null because when you create the snapshot callback it's using values that belong to that render. Similar to if you tried to access useState values in an event callback.
See here: https://stackoverflow.com/a/55265764/8846296
A solution is to use useRef to store your value instead of useState. And then you can access it in your callback using myValue.current
Upvotes: 0
Reputation: 315
So I managed to find a solution. The trick is to listen for state changes of messages with useEffect()
const [snapShot, setSnapshot] = useState(null);
const [messages, setMessages] = useState(null);
const [chat, setChat] = useState(props.chatId);
useEffect(() => {
const q = query(collection(db, "messages"), where("chat_id", "==", chat), orderBy("date","desc"), limit(5));
const unsuscribe = onSnapshot(q, (querySnapshot) => {
setSnapShot(querySnapshot)
});
return () => {
unsuscribe();
}
}, [chat]);
useEffect(() => {
if(messages === null){
console.log("setting messages the first time");
setMessages(snapShot.docs)
}else{
console.log("updating messages");
setMessages([...snapShot.docs, ...messages])
}
}, [snapShot]);
Upvotes: 11
Reputation: 599996
The querySnapshot
you get from Firestore always contains all snapshots that match the query, not just the changed/new documents.
So your onSnapshot
handler can be much simpler:
const unsubscribe = onSnapshot(q, (querySnapshot) => {
setMessages(querySnapshot.docs)
});
So: every time you get notified by Firestore that the data in q
has changed, you pass the data on to the state/UI for rendering.
Upvotes: 1
Reputation: 126
did you try this from the documentation
const q = query(collection(db, "cities"), where("state", "==", "CA"));
const unsubscribe = onSnapshot(q, (snapshot) => {
snapshot.docChanges().forEach((change) => {
if (change.type === "added") {
console.log("New city: ", change.doc.data());
}
if (change.type === "modified") {
console.log("Modified city: ", change.doc.data());
}
if (change.type === "removed") {
console.log("Removed city: ", change.doc.data());
}
});
});
Also the question is a bit confusing on what you want to achieve
Upvotes: 1