Firebase v9.8.4 deleteDoc Not Deleting from db - reactjs

I have a collection of Posts that are added to my db via this method:
const sendPost = async (event: any) => {
event.preventDefault();
if (loading) return;
setLoading(true);
const docRef = await addDoc(collection(db, "Posts"), {
id: tokenId,
postId: UID,
username: username,
profilePic: uri,
bio: bio,
likes: 0,
text: input,
timestamp: serverTimestamp(),
});
I have created a delete button with an onClick handler with the following code:
const handleDelete = async (e:any) => {
e.stopPropagation();
deleteDoc(doc(db, "Posts", post.postId));
console.log(post.postId);
}
The console.log in the above code matches the UID of the Document that im trying to delete, but nothing happens. Note: I added delete to the rules, and still no luck.
Does anybody know if I'm missing a step or can point me in the right direction??
Visual
Thanks!

You just don't know what is doc ID. Using addDoc(collection(db, "Posts"), data) function, you generate random document ID, and it is not an UID! To get that ID, you need to show how you get data from Firebase.
Using getDocs function, you can get doc ID like this:
async function getDocuments() {
const ref = collection(db, 'Posts')
const result = await getDocs(ref)
let data = []
if(result.exists()) {
result.foreach(docSnap => {
const doc = docSnap.data()
doc.docID = docSnap.id // here you getting real document ID
data.push(doc)
}
)
}
}
You have document ID above + Start Collection blue button, not in postID field!

Related

how do I post data to my nextjs api endpoint

I am making a test e-commerce sight to learn nextjs. I am trying to implement the checkout through stripe and I have it working if all of the information is static. when I make any of the information set to a variable it stops working and tells me that I am not passing any values into my variables. to test this I am making all of my data that needs to be passed, static data except for one which is when I get the error that I am not passing in information properly
obviously I am not sending the data to the api correctly. I think that my code looks just like the guides and docs so I am stuck. any help would be greatly appreciated.
here is the error message that I get:
"Missing required param: line_items[0][price_data][product_data][name]."
even if I change the state variable 'title' to a single value instead of an array, and in the updateState function settitle("title") I still get the same error
here is the front end code where I try to send the data to the api endpoint:
basket is an array of objects containing all of the products that the user has chosen.
const [id, setId] = useState([]);
const [price, setprice] = useState([]);
const [description, setdescription] = useState([]);
const [title, settitle] = useState([]);
const updateState = () => {
basket.forEach(element => {
setId([...id, element.id]);
setdescription([...description, element.description]);
setprice([...price, element.price]);
settitle([...title, basket.title]);
});
console.log(id);
console.log(description);
console.log(price);
console.log(title);
}
//send data to the api
const postData = async () => {
const response = await fetch("/api/checkout_sessions", {
method: "POST",
body: JSON.stringify(
id,
price,
description,
title,
),
});
return response.json();
};
return (
<form action="/api/checkout_sessions" method="POST">
<button
type="submit"
role="link"
className="button"
onClick={() => {
updateState;
postData;
}}
>
proceed to checkout
</button>
</form>
)}
here is the api code where I try to get that data and use it which is not working how I expect:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
export default async function handler(req, res) {
// var priceVariable ='price_1MB8P4FqoomU2P4qrVmtxCvp';
if (req.method === 'POST') {
const items = req.body.id
const amount = req.body.price
const description = req.body.description
const title = req.body.title
try {
// Create Checkout Sessions from body params.
const session = await stripe.checkout.sessions.create({
// shipping_options: ["shr_1MBn0HFqoomU2P4qZk4vqOQ3"],
shipping_address_collection: {
allowed_countries: ["US", "CA", "GB"],
},
line_items:[{
price_data: {
unit_amount: 1000,
currency: 'usd',
product_data: {
name: title,
description: "description",
},
},
quantity: 1,
}],
mode: 'payment',
success_url: `${req.headers.origin}/?success=true`,
cancel_url: `${req.headers.origin}/?canceled=true`,
});
res.redirect(303, session.url);
} catch (err) {
res.status(err.statusCode || 500).json(err.message);
}
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}
you can see in the line_items that everything is static except for the one variable that I am testing.
JSON.stringify expects an object (https://www.w3schools.com/js/js_json_stringify.asp)
const postData = async () => {
const response = await fetch("/api/checkout_sessions", {
method: "POST",
body: JSON.stringify({
id,
price,
description,
title,
}),
});
return response.json();
};
And on the api side you may have to parse the body before accessing any properties like so
const body = JSON.parse(req.body)
const title = body.title
(https://www.w3schools.com/Js/js_json_parse.asp)
It's unclear if the array/string mismatch is due to your testing changes, but you'll need to ensure a single string is supplied for name.
Your actual issue is likely here:
onClick={() => {
updateState;
postData;
}}
I'm surprised this is invoking the functions without (), but even if it were your postData() would start before the react state change happened.
I suspect if you initialized title with a value your endpoint would receive that.
const [title, setTitle] = useState("some default title");
You'll need to review how your application is tracking state here, and perhaps calculate that title and pass it through to the request alongside updating the state.

Fetch returns undefined the first time a user signs in

I sort of know why it happens, but not sure how to go on about solving it.
I have a React project that uses Cloud Firestore as database, and I have a simple login-page where you can sign in via your Google account. The first time you sign in a new document gets added to the "users" collection in Firebase.
After the document has been created it fetches that user data from Firebase and stores it in Redux.
const signInWithGoogle = async () => {
try {
const res = await signInWithPopup(auth, googleProvider);
const user = res.user;
const q = query(collection(db, "users"), where("uid", "==", user.uid));
const docs = await getDocs(q);
if(docs.docs.length === 0){
const firstName = user.displayName.split(' ')[0];
await addDoc(collection(db, "users"), {
uid: user.uid,
name: user.displayName,
firstName: firstName,
photoURL: user.photoURL,
authProvider: "google",
email: user.email,
})
dispatch(getUser(user))
}
} catch(err) {
console.error(err);
alert(err.message);
}
}
I also check whenever the user's auth state changes (here I also do another fetch and store it in Redux).
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
setLoading(false);
if(user){
dispatch(getUser(user))
} else {
console.log("user logout")
}
});
return unsubscribe;
}, []);
But when a new user signs in the first time, I get an error from the fetch:
export const getUser = createAsyncThunk("profile/getUser", async (user) => {
try {
const userQuery = query(
collection(db, "users"),
where("uid", "==", user?.uid)
);
const doc = await getDocs(userQuery);
const data = doc.docs[0].data();
return data;
} catch (err) {
console.error(err);
alert("An error occured while fetching user data");
}
});
"data" in above block is undefined for a small moment when the user signs in, so the alert in the try/catch block always goes off (it does manage to fetch the data after though).
This error only happens when it's a new user.
I understand that the fetch occurs before a document has been created in the "users" collection, but I'm not sure how to solve this. I've tried to add if/else to certain parts of the code (but just felt like I was grasping for straws).
I'm very much new to Firebase and still learning React, so every bit of help is really appreciated!
Problem is that your signInWithGoogle & useEffect both are running on user's auth status change. And, when its the new user, signInWithGoogle function makes aysnc call to create default doc, whereas useEffect runs to dispatch action, but at that moment user doesn't have any linked document. That is why you are getting undefined.
Ideally, you should remove one. You can merge the useEffect into signInWithGoogle to set the user details and dispatch as well.
const signInWithGoogle = async () => {
try {
const res = await signInWithPopup(auth, googleProvider);
const user = res.user;
const q = query(collection(db, "users"), where("uid", "==", user.uid));
const docs = await getDocs(q);
// create `doc` if its the new user
if(docs.docs.length === 0){
const firstName = user.displayName.split(' ')[0];
await addDoc(collection(db, "users"), {
uid: user.uid,
name: user.displayName,
firstName: firstName,
photoURL: user.photoURL,
authProvider: "google",
email: user.email,
})
}
// set user info and dispatch
setCurrentUser(user);
setLoading(false);
dispatch(getUser(user))
} catch(err) {
console.error(err);
alert(err.message);
}
}
Hope that answers your query.
Thanks

Firebase v9 signup a new user and store users in a users collection

I am able to sign up a new user, but how can I store that user in a collection in firestore? I am not sure with firebase v9 if the addDoc function is in the right place, but I don't know where else or how to code it.
export const useSignup = () => {
const [error, setError] = useState("");
const { dispatch } = useAuthContext();
const signup = (email: string, password: string, username: string) => {
setError("");
createUserWithEmailAndPassword(auth, email, password)
.then((res) => {
dispatch({ type: "LOGIN", payload: res.user });
const uid = res.user.uid;
const data = {
id: uid,
email,
username,
};
const ref = collection(db, "users");
addDoc(ref, {
data,
});
})
.catch((err) => {
setError(err.message);
});
};
return { error, signup };
};
To create a user-document for a newly signed-up user, you can do the following:
Access their bid from the user object (like you already did).
Create a document reference whose path ends in the above mid. Be aware that this document does not exist yet.
Use the setDoc method with above document reference, and user data to be stored as inputs.
It looks like the following in the form of code:
const uid = res.user.uid;
const data = {
id: uid,
email,
username,
};
const ref = collection(db, `users/${uid}`);
setDoc(ref, data)
.then(() => console.log("Created New User Document Successfully"))
.catch(() => console.log("Error"))
You must write the above code right after your dispatch call. Hope this helps!

Firebase firestore WEB v.9 retrieve data ID-s from collection where that current user ID contains

I am trying to retrieve specific data from firebase, in my redux store I have uniq id that I can get in any page like this
const currentUser = useSelector(selectLoggedInUser);
console.log(currentUser.id) // "71dc954d-d2a4-4892-8257-98696fe776cd" this is peace of doc name in "dms" collection
I want all doc-s that contains this ID "71dc954d-d2a4-4892-8257-98696fe776cd", how can I query it???
This is how I'm setting "dms" messages
export const sentDirectMsg = async ({ toUid, currentUid, message, name }) => {
const collecitonRef = await dmCollection(toUid, currentUid);
await addDoc(collecitonRef, {
timestamp: serverTimestamp(),
message,
name,
});
};
const dmCollection = async (toUid, currentUid) => {
const idPair = [currentUid, toUid].sort().join("_");
return collection(db, "dms", idPair, "messages");
};
I'm not enough clear sorry for that(just don't have enough experience), I'll try my best.
I'm trying to create Slack like app(I have many pages and function that I exporting from one place to another), I will show how I implement the channels messages sent & get from firebase, then explain how I make direct messages
//Function that sent message to exact channelId /channels/someChannelId/messages
// channelId is literal with dynamic id
export const sentMsg = async ({ name, message, channelId }) => {
await addDoc(collection(db, "channels", channelId, "messages"), {
timestamp: serverTimestamp(),
message,
name,
});
};
//Getting data from channel
const messagesRef = query(
collection(db, `channels/${channelId}/messages`),
orderBy("timestamp")
);
onSnapshot(messagesRef, (snapshot) => {
setMessages(snapshot.docs);
});
Now as I need DM I can't make it same way because it need some privacy, only 2 person should see the messages, so I need 2 uniq person that has uniq id and their collection of messages also is uniq(so that only they can see each other messages),in general when I register the users in my app I also save with them uniq ID for example this "71dc954d-d2a4-4892-8257-98696fe776cd",
//This is how I sent direct messages
// toUid - to whom I should sent
// currentUid - is who is sent
const sentDirectMsg = async ({
toUid,
currentUid,
message,
name,
}) => {
const collecitonRef = await dmCollection(toUid, currentUid);
await addDoc(collecitonRef, {
timestamp: serverTimestamp(),
message,
name,
});
};
const dmCollection = async (toUid, currentUid) => {
const idPair = [currentUid, toUid].sort().join("_");
return collection(db, "dms", idPair, "messages");
};
// As I'm sorting this two uniq ID-s from where person sent-s the message it is always same collection reference. My question is can I somehow by "query" or by "where" get all docs that contains current user ID???
Edited:
If I understood correctly, you want to get a document which id contains a part of the id you are looking for.
Using array-contains should do the trick:
const dmsRef = collection(db,"dms");
const docRef = query(dmsRef, where("idPair", "array-contains", id)); //for example id = "71dc954d-d2a4-4892-8257-98696fe776cd"
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
I based my example on this link from the official documentation.
If your data model is centered toward users being identified through their unique IDs, then you can store your data first hand to reflect his model:
const userData = {
name: 'Marouane',
state: 'BN',
country: 'TUN'
};
// Add the user document in collection `dms`, with the id being the user ID
const res = await db.collection('dms').doc('71dc954d-d2a4-4892-8257-98696fe776cd').set(userData);
You can then query the user document using its unique identifier:
Firebase v8
const userRef = db.collection('dms').doc('71dc954d-d2a4-4892-8257-98696fe776cd');
const doc = await userRef();
if (!doc.exists) {
console.log('No such user!');
} else {
console.log('User dms data:', doc.data());
}
EDIT (Added firebase v9 - modular):
import { getFirestore, ref, onValue } from "firebase/firestore";
const db = getFirestore(firebaseApp);
const userRef = ref(db, 'dms/71dc954d-d2a4-4892-8257-98696fe776cd');
onValue(userRef, (snapshot) => {
const data = snapshot.val();
console.log(data);
});
In case your document id is not known in advance, you can query for all available documents and filter out ones that does not match your user id:
import { getFirestore, collection, query, where, getDocs } from "firebase/firestore";
const db = getFirestore(firebaseApp);
const q = query(collection(db, "dms"));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
if (doc.id.startsWith('71dc954d-d2a4-4892-8257-98696fe776cd')) {
console.log(doc.data());
}
});
Meanwhile, this approach will cause great performance degradation and would be better traded with a storage re-design.

Posting an array as part of a Schema with React and Axios

I have the following Schema:
const SubmitDebtSchema = new Schema ({
balance: [{newBalance: Number, balanceDate: Date}],
});
What I want to do is make a post request from my React frontend using Axios, and save the 'newBalance' as a number pulled from state and balanceDate as today's date.
However, I can't figure out how to access the 'newBalance' and balanceDate.
If I try the following, it just posts an empty array to my database:
onSubmit = async (e) => {
e.preventDefault();
const dayCurrent = new Date().toLocaleString();
await axios.post("/api/submit/submitDebt", {
newBalance: this.state.balance,
balanceDate: dayCurrent,
})
this.props.history.push('/dashboard');
}
And similarly, if I try the following, it errors out:
onSubmit = async (e) => {
e.preventDefault();
const dayCurrent = new Date().toLocaleString();
await axios.post("/api/submit/submitDebt", {
balance.newBalance: this.state.balance,
balance.balanceDate: dayCurrent,
})
this.props.history.push('/dashboard');
}
So how do I access my Schema's newBalance and balanceDate, and append new entries rather than replace the original ones?
Any feedback would be appreciated!

Resources