When you call setDoc(docRef, data) (or docRef.set(data) in the older SDKs), you update the data for that document and get back a Promise<void>.
When you call addDoc(colRef, data) (or colRef.add(data) in the older SDKs), you create a new document within that collection and get back a Promise<DocumentReference>.
Neither option will return the data you passed in. Instead, you should be listening to the collection containing the new document using onSnapshot(colRef, observer) (or colRef.onSnapshot(observer) in the older SDKs). This observer will have its next() event handler fired with the collection's latest data as a QuerySnapshot object whenever it changes, including when you add data locally using a set/add/update/etc.
While this may seem counter intuitive, this allows you to use the following code to stamp out your collection of documents (and it will stay updated as you add/remove topics):
const db = /* firestore instance (e.g. from parameter) */;
const [topics, setTopics] = useState();
const [errorMsg, setErrorMsg] = useState();
useEffect(() => {
const colRef = collection(db, "topics");
return onSnapshot( // <-- onSnapshot creates and returns its unsubscriber
colRef,
{
next: (querySnapshot) => {
setTopics(
querySnapshot.docs
.map(topicDoc => ({
...topicDoc.data(), // <- this unwraps the snapshot's data into a normal object, along with its document ID
id: topicDoc.id
}))
);
setErrorMsg(null);
},
error: (err) => {
setTopics(null);
// TODO: handle database errors (e.g. permissions, connection loss, etc.)
// prefer Firebase error codes over the error's message if available
// (or the use the error itself if neither is available)
setErrorMsg(err.code || err.message || err);
}
}
);
}, [db]); // update whenever db is updated
// TODO: use errorMsg/topics as appropriate - such as:
if (topics === undefined) // undefined? still loading!
return (<div>Loading...</div>);
if (topics === null) // null? you've got an error to display!
return (<div>Error loading topics: { errorMsg || 'unknown error' }</div>);
if (!topics.length) // empty? show an empty table or other message!
return (<div>Error: No topics to display</div>);
// something else? good to render!
return (<>{
topics.map(
topicData => {
/* create topic element from topicData (don't forget the key!) */
return (
<div key={topicData.id}>
{topicData.country} - {topicData.name}
</div>
)
}
)
}</>);
thenof your write operation. Realtime listeners are automatically invoked after each remote or local update, and fit much better with React's reactive flow.