I want to update firebase database with react - reactjs

I have a problem while trying to update data in the firebase database with my react code.
Most probably my code syntax is not good, so can you help me in some way?
This is my code syntax:
const addNewData = async (e) => {
e.preventDefault();
let data = {
sifra:sifraRef.current.value,
naziv:nazivRef.current.value,
detalji_dijete:detaljiRef.current.value,
opis:opisRef.current.value,
broj_obroka:brojObrokaRef.current.value,
napomena:napomenaRef.current.value
}
const uuid = uid();
await updateDoc(collection(db, `namirnice/${uuid}`), data)
close();
}
All these examples I saw on youtube tutorials works...
I hope you can help me.

The updateDoc function is used to update an existing document. But since you call uuid(), you always get a new value and so you're trying to update a document that doesn't exist yet, which isn't possible.
To create a new document, use setDoc instead of updateDoc in your code.
Also see the Firebase documentation on setting a document

Related

Firebase data deleted upon page refresh in React

I have been stumped on a work around for this problem for a while now and was hoping someone could help.
I am currently working on a React UI that sends info to the backend Firebase for a budgeting app.
When the page first loads, I pull in the data using this:
const [incomeSources, setIncomeSources] = React.useState([]);
/////////////////////////////////
// PULL IN DATA FROM FIREBASE //
///////////////////////////////
async function getData() {
const doc = await getDoc(userCollectionRef);
const incomesData = doc.data().incomeSources;
// const expensesData = doc.data().expenses;
// const savingsData = doc.data().savingsAllocation;
// SET STATES //
if (incomesData.length > 0) {
setIncomeSources(incomesData);
}
}
then when I want to add a new object to the state array I use a input and button. The issue I currently have is that I have it set up like this:
async function updateFirebaseDocs(userID, stateName, state) {
const userRef = doc(db, "users", userID);
try {
await setDoc(userRef, { [stateName]: state }, { merge: true });
} catch (err) {
console.error("error adding document", err);
}
}
React.useEffect(() => {
updateFirebaseDocs(userID, 'incomeSources', incomeSources)
},[incomeSources])
this works so long as I don't refresh the page, because upon page refresh, incomeSources defaults back to an empty array on render. Causing firebase docs to become an empty array again which deletes firestore data.
I can't for the life of me figure out the workaround even though I know its probably right in front of me. Can someone point me in the right direction please.
Brief summary: I am able to pull in data from backend and display it, but I need a way to keep the backend database up to date with changes made in the Frontend. And upon refreshing the page, I need the data to persist so that the backend doesn't get reset.
Please advise if more information is needed. First time posting.
I have tried using the above method using useEffects dependency, I have also tried using localstorage to work around this but also don't can't think of a way of implementing it. I feel I am tiptoeing around the solution.

MongoDB Webhook function to save forminput in database

I've been trying to save data from my form in my MongoDB for some time.
I also get a response from the database.
See also: create object in mongo db api onclick sending form
Unfortunately there are not enough tutorials in my mother tongue and I don't seem to understand everything in English.
I've tried some of the documentation, but I always fail.
What is missing in my webhook function so that the form data can be stored?
exports = function(payload) {
const mongodb = context.services.get("mongodb-atlas");
const mycollection = mongodb.db("created_notifications").collection("dpvn_collection");
return mycollection.find({}).limit(10).toArray();
};
The Webhookfunction was totally wrong.
READ THE DOCUMENTATION FIRST
exports = function(payload, response) {
const mongodb = context.services.get("mongodb-atlas");
const requestLogs = mongodb.db("created_notifications").collection("dpvn_collection");
requestLogs.insertOne({
body: EJSON.parse(payload.body.text()),
query: payload.query
}).then(result => {
})
};

Async Clipboard API "ClipboardItem is not defined" - Reactjs copy image to Clipboard

I'm working on React js, I created my app with create-react-app using npm. I was trying to build a button that takes an image and writes it to the clipboard. Fourtunately I found this npm library that seems to work fine! But keeps me thinking why I couldn't use the ¿built-in? Asynchronous Clipboard API to copy the image (the text copy works fine). I read a really enlightening guide here, and kept reading other great guide here, so I tried all the codes suggested, there and in other pages (despite they don't seem to really change the functionality, I got to try). I came with the same error in every try that impedes to compile: "'ClipboardItem' is not defined no-undef". One code for example was this one:
const response = await fetch('valid img url of a png image');
const blob = await response.blob();
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob})]);
It seems to be simple, easy to follow. The problem is when you need to put the data in a form the Clipboard can read it, make it a blob, because I need the ClipboardItem constructor, and my app seems to be unable to recognize it as such. Keeps returning ClipboardItem is not defined or, if I somehow define it, says it's not a constructor, of course. I tried with other constructors like Blob(), but had the same problem. The last thing kept me thinking that, since I'm new in the programming world, if there is something kinda basic I don't know of the interaction of Web Apis like this one with node or Reactjs, and if there is a solution, of course! Thanks in advance, you guys are great!
Edit: adding the whole component code as requested:
import React from "react";
function TestingClipAPI () {
async function handleScreenshot () {
const response = await fetch('https://i.postimg.cc/d0hR8HfP/telefono.png');
const blob = await response.blob();
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob})]);
};
return (
<div>
<button onClick={handleScreenshot} id="buttonID">test</button>
</div>
)
};
export default TestingClipAPI;
Possible issue: This might be because of CRA (Create-React-App) config - similar issue. Something like the library linked can be done, create a canvas and copy the image from there.
Solution or a way to make it work anyway: make a call this way before using ClipboardItem:
const { ClipboardItem } = window;
Note: this also works with other constructors like toBlob and HTMLCanvasElement that had the same issue.
Things to look for:
Browser support Clipboard
Secure origin on HTTPS or localhost. See this post.
How the function is being called - in the OP's case - onClick & asynchronous.
The issue is that onClick are not asynchronous by default and you are not awaiting the response and you also have a typo in navigator.clipboard.
const handleScreenshot = async () => {
try {
const response = await fetch(
"https://i.postimg.cc/d0hR8HfP/telefono.png"
);
const blob = await response.blob();
await navigator.clipboard.write([
new ClipboardItem({ "image/png": blob }),
]);
} catch (err) {
console.error(err);
}
}
return (
<button onClick={async () => await handleScreenshot()} id="buttonID">
test
</button>
);
There are tradeoff between inline function and below are alternatives. I'd personally use the latter method.
function handleScreenshot() {
async function screenShot() {
try {
const response = await fetch(
"https://i.postimg.cc/d0hR8HfP/telefono.png"
);
const blob = await response.blob();
await navigator.clipboard.write([
new ClipboardItem({ "image/png": blob }),
]);
} catch (err) {
console.error(err);
}
}
screenShot();
}
return (
<button onClick={handleScreenshot} id="buttonID">
test
</button>
);
Lastly, you can return a chained promise.
Simply add window in front of ClipboardItem like the following
window.ClipboardItem(...)
Unfortunately, as of the time of this answer, ClipboardItem isn't supported in Firefox. (Support can be enabled via an about:config setting; but of course, most Internet users will not have done this.)
Source: https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem#browser_compatibility

Rendering a firestore timestamp in react

I have been trying for months to figure out how to display a firestore timestamp in react.
In December 2019, the solution I accepted on this post worked. It no longer works. I'm back to stuck.
I have a firebase.js helper to record dates with:
class Firebase {
constructor() {
app.initializeApp(config)
this.firestore = app.firestore();
this.auth = app.auth();
this.serverTimestamp = app.firestore.FieldValue.serverTimestamp;
}
I use that helper in forms to record the date an entry is created like so:
await Firebase.firestore.collection("blog").doc().set({
title: values.title,
createdAt: Firebase.serverTimestamp()
That correctly saves a date into the firestore document that looks like this:
When I hover over the date, it shows 'timestamp'. I am able to order the data returned from firestore by reference to the createdAt date - so it's curious that the timestamp can be used to sort the documents but cannot be used to print the date on the screen.
When it then comes to outputting the date, I am back to getting all the same errors I reported in the previous post - but the solution that worked in December, no longer works. I have seen a discussion in the firebase slack channel that a recent release of firebase has had some unintended consequences - is there any way to check if broken timestamps is one of them?
When I try:
{currentPost.createdAt.toDate().toISOString()}
TypeError: Cannot read property 'toDate' of undefined
When I try:
{currentPost.createdAt.toDate()}
TypeError: Cannot read property 'toDate' of undefined
When I try:
{new Date(currentPost.createdAt.seconds * 1000).toLocaleDateString("en-US")}
TypeError: Cannot read property 'seconds' of undefined
When I try (I know this won't work but just trying to find insights that might help solve this problem):
{currentPost.createdAt}
Error: Objects are not valid as a React child (found: object with keys
{seconds, nanoseconds}). If you meant to render a collection of
children, use an array instead.
I have seen some posts which say the date is undefined because the call on firebase hasn't returned a value yet, but when I console.log the whole currentPost, I get a value for created At, which is:
createdAt: t seconds: 1586495818 nanoseconds: 888000000
The part of this that looks suspicious is the "t". Does it represent something that I'm supposed to do something with in order to access the timestamp? Does anyone know how to investigate what the 't' represents?
I have seen this post and this post, and note that each of the answers to it suggest that the .toDate() extension should help to convert a firestore timestamp to something that can be output. None of those solutions work here.
Has anyone figured out a current solution that allows to both save and output a date from firestore?
Strange - I don't understand why or how this works, but it does.
I changed the useEffect to be async - as follows.
function ReadBlogPost () {
const [loading, setLoading] = useState(true);
const [currentPost, setCurrentPost] = useState({});
let { slug } = useParams();
useEffect(() => {
const fetchData = async() => {
try {
const response = await Firebase.firestore
.collection("blog")
.doc(slug)
.get();
console.log('response', response);
let data = { title: 'not found' };
if (response.exists) {
data = response.data();
}
setCurrentPost(data);
setLoading(false);
} catch(err) {
console.error(err);
}
};
fetchData();
}, []);
Then in the render, I can use:
{!loading && new Date(currentPost.createdAt.seconds * 1000).toLocaleDateString("en-US")}
Fingers crossed this works for more than a few months.
I was facing the same problem. The solution is simple for me, You have to first check if the timestamp exists or not.
{data.time === undefined ? (
<div>Time is not defined</div>
) : (
<div> {data.time.toDate().toLocaleTimeString("en-US")}</div>
)}
After that, you can do whatever you want.
(Frist time answering any questions, If i made any mistake, please do tell)
This worked for me:
{data.timestamp.toDate().toLocaleTimeString("en-US")}

add, set, update document with geofirestore

I am trying to use geofirestore https://geofirestore.com/ with react native, the fact is that I have already implemented the methods to add elements in firestore, however I have read in the documentation that the data have to be added by geofirestore methods. Do I have to change all the methods and use the firestore methods? I use javascript and not typeScript, I see that there are examples with typescript and it seems that they have updated the library and other examples do not work.
How could I add elements in the bbdd?
I used geofirex and it was very simple but it has no limit, I want to change to geofirestore to be able to use this feature.
Thank you!
so most if not all of the Typescript examples are almost identical to what you would do in JS (as well as the regular firebase library). Let me give you an example of adding and querying (with a limit) in react-native-firebase.
import firebase from 'react-native-firebase';
const doc = {
coordinates: new firebase.firestore.GeoPoint(0, 0),
name: 'The center of the WORLD'
};
const collection = firebase.firestore().collection('todos');
collection.add(doc).then((docRef) => {
collection.limit(100).get().then((querySnapshot) => {
// 100 items in query
querySnapshot.docChanges.forEach((change) => {
console.log(change.doc);
});
});
});
Now I've never used RNF, but from the docs I can gather that this is correct, and they do almost everything the same as the JS Firebase library (except docChanges returns as an array rather than a function that returns an array...). Anyway, lets see the same thing in Geofirestore with the added benefit of querying with limit and near a location!
import firebase from 'react-native-firebase';
import { GeoFirestore } from 'geofirestore';
const doc = {
coordinates: new firebase.firestore.GeoPoint(0, 0),
name: 'The center of the WORLD'
};
const geofirestore = new GeoFirestore(firebase.firestore());
const geocollection = geofirestore.collection('todos');
geocollection.add(doc).then((docRef) => {
geocollection.limit(100).near({
center: new firebase.firestore.GeoPoint(0, 0),
radius: 10
}).get().then((querySnapshot) => {
// 100 items in query within 10 KM of coordinates 0, 0
querySnapshot.docChanges().forEach((change) => {
console.log(change.doc);
});
});
});
Anyway, don't be afraid of the Typescript code samples, if you just strip the : GeoFirestore or whatever it's valid JS...
// This in TS
const firestore = firebase.firestore();
const geofirestore: GeoFirestore = new GeoFirestore(firestore);
// Is this in JS
const firestore = firebase.firestore();
const geofirestore = new GeoFirestore(firestore);
Finally I try to keep this viewers app up to date with Vanilla JS, if that helps.
This works perfectly
await eventRef.update({'d.arrUsers':
firebase.firestore.FieldValue.arrayUnion(userUid)});
However I am unable to make this work, it is a map and I want to add another user, before it worked correctly...
await eventRef.set({'d.userMap': {
[userUid]: {
name: name,
age: age
}
}}, {merge: true});
If I use update it deletes the one that exists and puts the new one, if I do so it gets it out of d in the document

Resources