Read Data from Firestore and React custom hooks for realtime data.
Prequisites
- React Custom Hooks
- Firebase Initialization
Contents
Firestore Read Methods
- reference and doc()
- getDoc() and snapShot
- collection(), query() and getDocs()
- onSnapshot and realtime data subscription
React custom hooks for realtime data subscription
- useDocument()
- useCollection()
- useSpecificCollection()
Custom Hooks in React
Firestore Read Methods
Firestore is a NoSQL database service offered by google's Firebase.
Things You Need to know about firestore methods
- reference and doc()
- getDoc() and snapShot
- collection(), query() and getDocs()
- onSnapshot()
Reference and doc()
A reference points to a place in Firestore database.
doc() is a function provided by firestore to get a reference in the Database.
The Reference itself does not contain any data, It just points to a place in Database where data may or may not exists.
doc() takes in three arguments
db
instance initialised by getFirestore(app)key
of a collectionkey
of a document
const messageReference = doc(db, "messages", "messageId")
now we have reference which points to a particular message reference where document may or may not exists
getDoc() and snapShots
Now that we have a reference, we can get the document by passing the reference as an argument to the getDoc() function. Remeber getDoc() returns a promise.
const messageSnapShot = await getDoc(messageReference);
now snapShots has two methods which you need to know.
exists()
- check if document exists -returns booleandata()
- get data of the document -returns data
now you have learnt to get a single document from firestore.
collection(), query() and getDocs()
Now what if you want multiple documents from a collection ?
collection()
is similar to doc()
but takes only two arguments -
db
instancekey or id
of a collection in database
const messagesCollectionReference = collection(db, "messages")
it also returns a reference to an entire collection.
query()
is function to get reference(query) to documents in a collection which matches a specific condition specified by where() function.
It takes two arguments
collectionReference
which we got abovewhere()
function which itself takes 3 arguments- A "key" in the document example "author"
- an "operator" as a string example "=="
- A value to equate to example "LoggedInUserId"
putting it all together -
const messagesLoggedInUserQuery = query(messagesCollectionReference, where("author", "==", LoggedInUserId))
now getDocs()
is similar to getDoc()
, but getDocs will return an iterator of all the snapshots matching the query.
const messagesFromUserSnapshots = await getDocs(messagesLoggedInUserQuery)
(()=>messagesFromUserSnapshots.forEach(snapshot=>console.log(snapshot.data()))
()
You can also push all the snapshot.data() to an array defined before.
To get All Documents from a collection without any condition, just omit the where() as the second argument in query().
onSnapshot()
onSnapshot() is a listener provided from firestore. it takes two arguments
singleDocumentReference
orquery
- A
callback
function which runs everytime any data pointed by the first argument changes
The CallBack also runs once on initialition it is passed an argument by the listener(), either a single snapshot or an array of snapshots based on first argument
The onSnapshot() itselfs returns a cleanup(Unsubscriber) function which needs to be called to end the data subscription
const unSubscribe = onSnapshot(messageReference, callback);
///do something
unSubscribe()
Now we know how to get single and multiple documents from firestore and also subscribe to realtime data. Now we can procced to building hooks.
React Custom Hooks for Realtime Subscription
Now that we know all the required firestore methods, we have to tackle react specific methods to initialize realtime subscription
The Requirements are
- We have to call the onSnapshot Listener on Component First Mount
- We have to extract and store data in a state variable
- onSnapshot init-> on db change -> Callback -> updateState -> updateView
- Then we have to destroy the listener on component unmount
Lets tackle the above requirements step by step
- To Initialize onSnapshot only once on component mount we have to use useEffect with empty dependency array
- To store the Data we can use useState hook
- To Destroy the listener we can return unSubscribe method from useEffect, which will be used for cleanUp
Single Document Hook
Single DocumentHook can be a re-usable hook , which takes, collectionKey and documentKey as Parameter
// Initialize App and Database before
const useDocument = (collectionKey, documentKey) => {
const [data, setData] = useState(null);
useEffect(() => {
const documentReference = doc(db, collectionKey, documentKey);
const unSubcribe = onSnapshot(documentReference, (documentSnapshot) => {
const newData = documentSnapshot.data();
setData(newData);
})
return unSubcribe // cleanup Function when component unMounts
}, [])
return data; // return State Variable which can be consumed in the component
}
Full Collection Hook
Full CollectionHook can also be a re-usable hook , to listen to entire collection by usong empty query, which takes, documentKey as Parameter
// Initialize App and Database before
const useDocument = (documentKey) => {
const [data, setData] = useState(null);
useEffect(() => {
const q = query(collection(db, documentKey));
let newData = [];
const unSubcribe = onSnapshot(q, (querySnapshot) => {
querySnapshot.forEach(snapshot=>newData.push(snapshot.data()));
setData(newData);
})
return unSubcribe // cleanup Function when component unMounts
}, [])
return data; // return State Variable which can be consumed in the component
}
Specific Query Hook
Specific Query Hook can be set up once with a custom query , it does not take any parameters, as query will be already defined ex- listen to messages of logged in user
// Initialize App and Database before
const useDocument = () => {
const [data, setData] = useState(null);
useEffect(() => {
const q = query(messagesCollectionReference, where("author", "==", LoggedInUserId);
let newData = [];
const unSubcribe = onSnapshot(q, (querySnapshot) => {
querySnapshot.forEach(snapshot=>newData.push(snapshot.data()));
setData(newData);
})
return unSubcribe // cleanup Function when component unMounts
}, [])
return data; // return State Variable which can be consumed in the component
}
Thats it Folks, Note - At the time of writing, firebase version referenced is 9.8.0
Subscribe to my newsletter
Read articles from Vishnu Aithal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by