I am building to do list and I have complete button where I want to toggle true or false state and updating in firebase. I was searching on the platform, there were simillar solutions but not exacly what I needed.
So far I have this function:
const completedHandler = (id) => {
const docRef = doc(db, "todos", id);
updateDoc(docRef, {
isCompleted: !initialValues.isCompleted
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error.message);
});
};
The problem is that this is updating the state only once, and it won't change it again, for example if the state is false, it is updating in firebase as true, and that's it.
I am not sure where is the issue. Any help is appreciate.
Since the value that you write to the database depends on the current value of the field in the database, you'll need to first read the data and then write the new value based on that. The best way to do this is with a transaction and would look something like:
import { runTransaction } from "firebase/firestore";
try {
const docRef = doc(db, "todos", id);
await runTransaction(db, async (transaction) => {
const todoDoc = await transaction.get(docRef);
if (!todoDoc.exists()) {
throw "Document does not exist!";
}
const newValue = !todoDoc.data().isCompleted;
transaction.update(docRef, { isCompleted: newValue });
});
console.log("Transaction successfully committed!");
} catch (e) {
console.log("Transaction failed: ", e);
}
Related
fetchedAuthor is a author object. isFollow is his follower count. when someone clicks on folow isFollow changes. when isFollow changes i want to rerun useEffect. when the author changes, the fetchedAuthor changes but i dont want this useEffect to reRun as this is strictly for follower handling not author handling but at the same time when the author changes i want this useEfffect to know that author has changed so the next time when isFollow changes the useEffect doesnt fetch with the previous fetchedAuthor but the latest value of fetchedAuthor.
useEffect(() => {
setCurrentAuthor(fetchedAuthor) ;
},[fetchedAuthor]) ;
useEffect(async () => {
try {
const response = await fetch(`URL/${currentAuthor}/${isFollow}`);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}},[isFollow]) ;
would this help me to get the appropriate response ?
Use a ref to store the current fetchedAuthor. Update the ref whenever fetchedAuthor changes. Use the ref's value when calling the api:
const authorRef = useRef(fetchedAuthor);
useEffect(() => {
authorRef.current = fetchedAuthor;
}, [fetchedAuthor]);
useEffect(async() => {
try {
const response = await fetch(`URL/${authorRef.current}/${isFollow}`);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}, [isFollow]);
I was following this tutorial https://www.sitepoint.com/reddit-clone-react-firebase/.
Earlier it was really simple just call onSnapshot after the document is fetched, but here it is a separate function, and now here comes the issue, when I try to call that onSnapshot by passing document, but it always says,no matter what type of data I tried to pass it as the first param, it always says, it is not type of 'bc' which it expects.
FirebaseError: Expected type 'bc', but it was: a custom he object
useEffect(async () => {
const postsCollection = collection(db, "posts");
const orderedCollection = query(
postsCollection,
orderBy("createdAt", "desc")
);
try {
onSnapshot(
orderedCollection, // here I think the problem is!
(querySnapshot) => {
console.log("yahaan se problem hai");
console.log(querySnapshot);
const _posts = [];
querySnapshot.forEach((doc) => {
console.log(doc);
_posts.push({
id: doc.id,
...doc.data(),
});
});
console.log(_posts);
// setPosts(_posts);
},
(error) => {
console.log("error occured: ", error);
},
() => {
console.log("completed");
}
);
} catch (e) {
console.log("ye kya drama hai:", e);
} finally {
console.log("finally");
}
}, []);
Okey, so I had the same problem and I found a solution after struggling with the newest version of firebase for a while.
I don't know if you're using a class component or a functional one, in this example i'm using a funcional component but I assume it'll work the same if you replace the react hooks.
import { getFirestore, collection } from 'firebase/firestore'
const db = getFirestore();
const colRef = collection(db, "team")
const [results, setResults] = useState([]);
useEffect(() => {
let isMounted = true;
onSnapshot(colRef, (snapshot) => {
if (isMounted) {
const results= snapshot.docs.map((doc) => {return {...doc.data(), id: doc.id}});
setResults(results)
}
});
return () => { isMounted = false };
}, []);
This way your component'll listen to updates everytime the data changes, after that you can personalize it using querys but i wanted to show you a simple example so it's easy to understand.
I had the same problem, unfortunately, the above didn't help me. in my case I was actually importing form functions and types from '#firebase/firestore' and others from 'firebase/firestore'... this was done by autoImport. the moment I made all of them get the types and functions from the same place it worked instantly
These kind of errors occur usually when the functions aren't used the way they're supposed to. I can't really tell where the problem comes from in your code but you may try the getDocs method instead and a state variable to store your values
try this code.
const [Results, setResults] = useState([]);
useEffect(() => {
const FetchedPosts = async () => {
const querySnapshot = await getDocs(
collection(db, 'posts'),
orderBy("createdAt", "desc")
);
querySnapshot.forEach((doc) => {
setResults((prevState) => [...prevState, doc.data()]);
});
};
FetchedPosts();
}, []);
All I want to do is to get a row (so called 'doc') from a data base.
so far, I have tried:
all with the 'aref'
const aref = firebase
.firestore()
.collection("polja")
.where("id", "==", match.params.id);
console.log(aref);
function getIt() {
const item = [];
setLoading(true);
aref.get().then((doc) => {
const data = doc.data();
setItem(item);
console.log(item);
setLoading(false);
});
}
useEffect(() => {
getIt();
}, []);
this gave the following error:
To get a single document, you must specify the document ID:
firebase.firestore().collection("polja").doc(documentId).get().then((snapshot) => {
console.log(snapshot.data())
}).catch((e) => console.log(e))
Also you should not use .where() to get just a single document, but there is an issue I found in your original code.
If you look carefully, the parameter in .where() is a string "match.params.id". That seems to be a dynamic value being fetched from somewhere else. Please remove the quotes and try again.
firebase.firestore().collection("polja").where("id", "==", match.params.id).get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
Try adding a catch block as shown which might help catch any errors. Make sure your security rules also allow you to fetch the data.
Also if any error is logged in the console, share a screenshot of it.
To get single document form firebase firestore you should first know if you are using the modular firebase 9.+ version or firebase version < 9.
In new modular firebase firestore(version 9.+) it should be like this:
import { getFirestore, collection, query, getDocs } from 'firebase/firestore'
async read(id) {
const firestore = getFirestore()
const docRef = doc(firestore, this.collectionPath, id)
const docSnap = await getDoc(docRef)
const data = docSnap.exists() ? docSnap.data() : null
if (data === null || data === undefined) return null
return { id, ...data }
}
If you are using not modular Firebase firestore( < version 9) then the same function should look something like this:
async read(id) {
const result = await (await firestore())
.collection(this.collectionPath)
.doc(id)
.get()
const data = result.exists ? result.data() : null
if (data === null || data === undefined) return null
return { id, ...data }
}
I've been searching and looking for an answer everywhere for days and still can't figure this out.
First let me show you what i want to achieve, here is the picture of the program:
And here is the picture of my Firestore database:
I want to be able and check if the user puts in the roomID in this case 5575 like in the picture above, the program should first check if that roomID exists in the firestore database. When i click the button Join i dont get anything in the console.
Here is my code so far:
const name12 = document.getElementById("oponentname") ;
const roomid = document.getElementById("idinput");
const joinButton = document.getElementById("joinButton");
var firebaseConfig = {
my configuration is here
}
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();
joinButton.addEventListener("click", function(){
if (roomid.value == db.collection('game').doc(roomid.value).get()) {
console.log("Succes");
}
});
I think you should fix it like this:
db.collection("game")
.doc(roomid.value)
.get()
.then((doc) => {
if (doc.exists) {
console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
.catch(function (error) {
console.log("Error getting document:", error);
});
You can read more here
You have to query for documents with that roomID and then check if one matches.
const get_room = (id) => {
return db.collection('game')
.doc(id)
.get()
.then(function(doc) {
return doc.exists
})
.catch(function(err) {
console.log(err)
})
Note that this is an async function, so to check if the inputed room id exists you should use a promise or async/await. My implementation uses promises.
joinButton.addEventListener("click", function(){
get_room(roomid.value).then((doc) => { if (doc) console.log("Succes"); })
});
I am trying to write a function in Cloud Functions that triggers every time a user gets created and which then saves that user into a list of users and finally increments a user counter.
However I am not sure if I am using promises correctly.
exports.saveUser = functions.auth.user().onCreate(event => {
const userId = event.data.uid
const saveUserToListPromise = db.collection("users").doc(userId).set({
"userId" : userId
})
var userCounterRef = db.collection("users").doc("userCounter");
const transactionPromise = db.runTransaction(t => {
return t.get(userCounterRef)
.then(doc => {
// Add one user to the userCounter
var newUserCounter = doc.data().userCounter + 1;
t.update(userCounterRef, { userCounter: newUserCounter });
});
})
.then(result => {
console.log('Transaction success!');
})
.catch(err => {
console.log('Transaction failure:', err);
});
return Promise.all([saveUserToListPromise, transactionPromise])
})
I want to make sure that even if many users register at once that my userCounter is still correct and that the saveUser function won't be terminated before the transaction and the save to the list has happened.
So I tried this out and it works just fine however I don't know if this is the correct way of achieving the functionality that I want and I also don't know if this still works when there are actually many users triggering that function at once.
Hope you can help me.
Thanks in advance.
The correct way to perform multiple writes atomically in a transaction is to perform all the writes with the Transaction object (t here) inside the transaction block. This ensures at all of the writes succeed, or none.
exports.saveUser = functions.auth.user().onCreate(event => {
const userId = event.data.uid
return db.runTransaction(t => {
const userCounterRef = db.collection("users").doc("userCounter")
return t.get(userCounterRef).then(doc => {
// Add one user to the userCounter
t.update(userCounterRef, { userCounter: FirebaseFirestore.FieldValue.increment(1) })
// And update the user's own doc
const userDoc = db.collection("users").doc(userId)
t.set(userDoc, { "userId" : userId })
})
})
.then(result => {
console.info('Transaction success!')
})
.catch(err => {
console.error('Transaction failure:', err)
})
})