I am creating a Svelte application using Firebase Database and am trying to convert a simple Firebase call to use a Query Snapshot, rather than a Get, so that I can instantly see changes on screen if the database changes.
I have some working code but it is currently not updating as I need it to and when I use the Query Snapshot code it stops working almost completely. For the purposes of this – I am simply trying to get a list of Users from the database and display them on a page, which updates if any new users are added or details are changed without the need to refresh. I will list both the working and non-working code below:
Working Code
allUsersStore.JS
import { fetchUsers } from "@api/allUsers";
import { onMount } from "svelte";
import { writable } from "svelte/store"
export function allUsersStore() {
const users = writable([]);
const loading = writable(false);
onMount(loadUsers);
async function loadUsers() {
loading.set(true);
try{
const {users: theUsers} = await fetchUsers();
// console.log(theUsers)
users.set(theUsers);
} catch(e) {
console.log("ERROR!" + e.message);
}
finally {
loading.set(false);
}
}
return {
// assignedUser: { subscribe: assignedUser.subscribe },
users: { subscribe: users.subscribe },
loading: { subscribe: loading.subscribe },
// users
}
}
allUsers.js
import { db } from "@db/index";
import { getCountFromServer, Timestamp, doc, collection, getDocs, query, addDoc, orderBy, getDoc, where, setDoc, onSnapshot, updateDoc, deleteDoc, arrayUnion, arrayRemove } from "firebase/firestore";
async function fetchUsers() {
const q = query(collection(db, "userData"), orderBy('fullName', 'asc'));
const qSnapshot = await getDocs(q);
const users = qSnapshot.docs.map(doc => {
const user = doc.data();
return {...user, id: doc.id}
});
return {users};
}
+page.svelte
<script>
import { allUsersStore } from "@stores/allUsersStore";
const { users, loading} = allUsersStore();
</script>
{#each $users as user (user.id)}
<h2>{user.fullName} </h2>
{/each}
This code works fine to produce a list of Usernames but, as I mentioned above, it will not update if a change to the database is made. As I come from an IOS background I know about Query Snapshots so I tried to implement that solution. All I changed was the call on the allUsers.js page as below:
async function fetchUsers() {
const q = query(collection(db, "userData"), orderBy('fullName', 'asc'));
const unsubscribe = onSnapshot(q, (querySnapshot) => {
const users = [];
querySnapshot.forEach((doc) => {
users.push(doc.data());
});
for (const x of $users) {
console.log(x.fullName)
}
return {users};
});
}
The console.log is showing the list of users names and I can see this list updating when I trigger a change in the database, however I am now getting an error from the try/catch block on allUsersStore.JS and no list is produced on my +page.svelte. The error is:
Cannot destructure property ‘users’ of ‘(intermediate value)’ as it is undefined
Could some kind person please explain to me what I am doing wrong and if there is a better practice for what I am trying to achieve please?
Thanks in advance 🙂