Still learning Firebase. I'm creating a forum and I'm trying to save the data in Firebase so the data won't disappear when I refresh the page. I added some test data and I got it working just fine. However, when I replace my test data with an array I'm working with that is constantly updated via useState I just get an empty array saved inside Firebase and all the topics I made disappear again when I refresh the page.
Upon clicking a Create Topic button the user inputs a topic title and a message. Everything is stored inside the topic array and each topic is saved as an object inside that array. The object holds a few values like the title of the topic, the user who created it and the date. The message is also saved however I'm just working on the front page for now which displays a list of all the topics. I then have some code which maps through the array and sorts / displays the topics on the main page.
const [topic, setTopic] = useState([]);
const [title, setTitle] = useState();
const [message, setMessage] = useState();
const addTopic = (e) => {
e.preventDefault();
setTopic([
...topic, // the array
{
id: 4,
title: title,
message,
author: "Dagger",
count: 1,
date: new Date(),
},
]);
try {
const docRef = addDoc(collection(firestore, "topics"), {
topic, // the array I'm trying to add
});
console.log("Document written with ID: ", docRef.id);
} catch (e) {
console.error("Error adding document: ", e);
}
};
The problem is that you are using the topic variable which hasn't been updated yet. You should note that useState doesn't update the variable immediately.
You can do this instead.
const [topic, setTopic] = useState([]);
const [title, setTitle] = useState();
const [message, setMessage] = useState();
const addTopic = (e) => {
e.preventDefault();
const updatedTopic = [
...topic,
{
id: 4,
title: title,
message,
author: "Dagger",
count: 1,
date: new Date(),
}
];
setTopic(updatedTopic);
try {
const docRef = addDoc(collection(firestore, "topics"), {
updatedTopic,
});
console.log("Document written with ID: ", docRef.id);
} catch (e) {
console.error("Error adding document: ", e);
}
};
Related
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.
I've been working on React/Redux app with firestore database
In my app I have simple POST request sent when the user send a message in the input field
and the data the user enters supposed to render in the same page without the need to refresh but I do still need to refresh even without deps in my useEffect!
Here's my code :
Post component
{posts.length > 0 &&
[...posts].map(({ id, data: { message, name, job, avatarUrl } }) => (
<Post
key={id}
name={name}
job={job}
message={message}
avatarUrl={avatarUrl}
/>
))}
However I also encounter a weird behavior after I refresh which is the components are rendered twice!Although my database be containing only one unique data for each messageThe react app renders it twice ( The querySnapshot from the database being added to the state arrayposts twice
useEffect
useEffect(() => {
querySnapshot();
});
}, []);
Database query:
const q = query(
collection(db, "posts"),
where("type", "==", "post"),
orderBy("postDate", "desc")
);
Retrieving the data :
const [input, setInput] = useState("");
const [posts, setPosts] = useState([]);
const [nextId, setNextId] = useState("0");
const addPost = (e) => {
e.preventDefault();
const docData = {
name: "mo",
job: "zoo",
message: input,
avatarUrl: "https://",
postDate: firebase.firestore.FieldValue.serverTimestamp(),
type: "post",
};
setDoc(doc(db, "posts", nextId.toString()), docData);
setNextId(parseInt(nextId) + 1);
setInput("");
};
async function querySnapshot() {
const querySnapshot = await getDocs(q);
console.log(querySnapshot.docs[0].data().message);
setNextId(querySnapshot.docs.length)
querySnapshot.docs.forEach((doc) => {
let data = {
id: doc.id,
data: doc.data(),
};
if (data && !posts.includes(data.id)) {
setPosts((current) => [...current, data]);
console.log("psts now", posts);
}
});
}
I tried to use the JavaScript Set by creating
useState(new Set()) but the same problem occurred of duplicate elements
I also tried to change deps of useEffect to render when the posts state array changes still not rendering untill I refresh
The duplication reason would be caused by setPosts(), I have updated the code as below, try to avoid setting the value inside the loop.
async function querySnapshot() {
const querySnapshot = await getDocs(q);
setNextId(querySnapshot.docs.length)
const data = querySnapshot.docs.map((doc)=>{
return {id:doc.id, data: doc.data()}
})
setPosts((current) => [...current, data])
}
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!
This is for a LinkedIn clone.
I have been trying to order my array of posts with the orderBy() method but it isn't being implemented. When I submit my post, it will go into a random order.
The post should go in descending order based on the timestamp. The const q variable should be controlling my order but it isn't even being used.
const [input, setInput] = React.useState('')
const [posts, setPosts] = React.useState([])
React.useEffect(() => {
const postCollection = collection(db, 'posts')
const q = query(postCollection, orderBy('time', 'desc'))
console.log(q)
onSnapshot((postCollection), q, snapshot => (
setPosts(snapshot.docs.map(doc => (
{
id: doc.id,
data: doc.data()
}
)))
))}, [])
const sendPost = (e) => {
e.preventDefault()
addDoc(collection(db, 'posts'), {
name: 'John Doe',
description: 'This is a test',
message: input,
photoUrl: '',
time: serverTimestamp()
},)
setInput('')
}
Those are the two sections that control the posts when the submit button is clicked and the post renders to the page. I render my post by clicking the 'enter' key and it will render like a LinkedIn post.
What I am trying to do here is to extract the id (Number) from an API response, parse it into String, and set it to its state variable. However, when I console log it, it gives me an empty string, which is the default value of it. If anyone could give me a suggestion or tell me what's wrong, that would be greatly appreciated. My briefly reproduced code snippet is down below:
const [userId, setUserId] = useState<string>("");
const getMyUserId = async () => {
const { data: objects } = await axios.get("/");
const user_id = objects.objects[0].id.toString();
setUserId(user_id);
console.log("userId", userId); <- output is empty string
};
const getMyCalendarId = async () => {
const url = `/${userId}/cal/calendars`;
const { data: objects } = await axios.get(`${url}`);
const calendar_id = objects.objects[0].id.toString();
setCalendarId(calendar_id);
};
useEffect(() => {
getMyUserId(); <- render every time page is loaded
getMyCalendarId
}, []);
To retrieve the user id you should access data, instead of objects. Like this:
const user_id = data.objects[0].id.toString();
objects is the typing of data, it is not the actual property.