React Native Firebase transaction not working properly - javascript

I'm developing an App using React Native and Firebase Real-Time Database.
In the database, I have some data as below.
myRoot
|
|--myValue1: 0
|
|--myValue2: 2
Then, I want to increase those values by one and get the following result.
myRoot
|
|--myValue1: 1
|
|--myValue2: 3
For this, I used a function as follows.
myFunction = () => {
const myRef = firebase.database().ref('myRoot');
return myRef.transaction((data) => {
data.myValue1++;
data.myValue2++;
return data;
})
.then(console.log('Done'))
.catch((error) => console.log(error));
}
But, after calling the function, I get the following error.
[Unhandled promise rejection: TypeError: null is not an object (evaluating 'data.myValue1')]
I tried taking a snapshot of myRef and print it in the console. It worked and printed the current values. But, transaction does not work and gives null.
Please help me to solve this problem.

You'll need to check data for null, as this happens the first time the transaction handler function is called. The handler will get called again with the snapshot, if it's present at the location of the database where you are performing the transaction. You can see this check being performed in the sample code in the documentation.

Something like this I believe Sennen:
myFunction = () => {
const myRef = firebase.database().ref('myRoot');
myRef.transaction((data) => {
if(data) {
if(data.myValue1) {
data.myValue1++;
}
if(data.myValue2) {
data.myValue2++;
}
}
return data;
})
.then(console.log('Done'))
.catch((error) => console.log(error));
}

Found what caused the problem. The code segment is fine. The problem was in the dependencies. Just execute npm install firebase --save and then npm install. Restart the project. It worked!

Related

I am having an issue when trying to retrieve data from Firestore using the Firebase JS SDK

I am having an issue when trying to retrieve data from Firestore using the Firebase JS SDK. I am receiving the following error:
TypeError: firebase_firestore__WEBPACK_IMPORTED_MODULE_3__.getDoc(...).data is not a function
I am trying to load the data using the following code:
useEffect(() => {
const getAuthorData = async () => {
setAuthorData(
await getDoc(doc(db, 'users', post.data.author)).data()
)
}
const p = getAuthorData()
console.log(p)
}, [])
I think have imported the necessary Firebase modules and initialized the app with the correct configuration. I have also checked that the getDoc function is returning a Firestore DocumentSnapshot object, but the data() method is not a function. Some data is showing behin the error
I would like to know if there is a problem with my code or if I am missing something else.
Any help would be appreciated, thanks!
The problem is that getDoc returns an object that does not have a method called data. It returns a promise. Your code right now is trying to call data() on that promise instead of first awaiting it. If you are trying to keep this at one line of code, you will have to force the await to happen first before the call to data() by using parenthesis:
(await getDoc(doc(db, 'users', post.data.author))).data()

Firebase callable function to read real time database

Here I am trying to access the user's data from real time database by providing the UID. I have tried so many things but none worked. I have followed the documentation but no luck I am keep getting error -
Sending back results [promise]
Another example for writing the data which I have followed to create my logic but it didn't worked -
exports.userData = functions.https.onCall((data, context) => {
// verify Firebase Auth ID token
if (!context.auth) {
return { message: 'Authentication Required!', code: 401 };
}
const userId = data.text;
const ref = database.ref('/USERS/' + userId);
return ref.on('value', (snapshot) => {
console.log(snapshot); /* <--- I have tried with this and without this none worked*/
})
.then(snapshot => {
return {
data: snapshot
};
}).catch((error) => {
throw new functions.https.HttpsError('unknown', error.message, error);
});
});
The error I get on client side is -
service.ts:160 POST https://us-central1-gokuapp.cloudfunctions.net/userData 500
error.ts:66 Uncaught (in promise) Error: INTERNAL
at new YN (error.ts:66)
at XN (error.ts:175)
at rC.<anonymous> (service.ts:231)
at tslib.es6.js:100
at Object.next (tslib.es6.js:81)
at r (tslib.es6.js:71)
Edit: Before, I was correctly writing the code on my end but, I was either getting the error or null object based on the changes that I made during the discovery process. Anyone who had faced the same problem, just remember this... "cloud functions takes time to warm up to get fully functional", even though I am really thankful to #Frank van Puffelen and #oug Stevenson for their input. :) :) :)
Don't use on() in Cloud Functions, since that attaches a persistent listener to a query (and it doesn't return a promise). Use once() instead to query data a single time and get a promise the resolves with a snapshot. Also you should use snapshot.val() to get a plain JavaScript object with the contents of the snapshot.
return ref.once('value') // use once() here
.then(snapshot => {
return {
data: snapshot.val() // also use val() here to get a JS object
};
})
.catch((error) => {
throw new functions.https.HttpsError('unknown', error.message, error);
});
Callable Cloud Functions can return any JSON data. Your snapshot variable is a DataSnapshot object however, which contains a lot more than just JSON data.
You're probably looking to return the snapshot's value:
.then(snapshot => {
return {
data: snapshot.val()
};

Parsing JSON's from a server using React and Express - "TypeError: Cannot read property '' of undefined"

I am having issues parsing a JSON returned from my server, in my client code. If I send a basic request to my mongoDB server:
GET http://localhost:4000/food/
I get the following response, which is obviously an array of objects.
In my client, I have a state defined in the constructor:
this.state = {
index: 0,
foodList: []
};
And a function, callServer, which is called when the page is loaded using componentWillMount():
callServer() {
fetch("http://localhost:4000/food/")
.then(res => res.json())
.then(res => this.setState({ foodList: res }))
.catch(err => err);
}
This function populates the foodList in the files state with the server output - when I run console.log("debug:\n" + JSON.stringify(this.statefoodList[0])) the output is
Debug:
{"registerDate":"2020-04-01T14:34:04.834Z","_id":"5e66437d59a13ac97c95e9b9","image":"IMAGE","name":"Example 2","address":"BI1 111","type":"ExampleType1","price":"£Example Price","link":"example.com"}
Which shows that foodList is correctly set to be the output from the server.
The issue is, if I perform console.log("debug:\n" + JSON.stringify(this.state.foodList[0].name)) I get the error TypeError: Cannot read property 'name' of undefined.
I've been struggling with this issue for a while now - I do not understand why the client believes foodList to be undefined when you can see from prior testing that it is not undefined, and it is in a JSON format.
As a side note, if it is important, I call console.log() from inside the render() method, but before the return() value.
I'm very new to the React framework and JS as a whole, so any help would be appreciated :)
So, a good thing to note in react is that the state changes happen asynchronously. Another good thing to note is that chrome likes to be helpful with console logs and will show what the values evaluate to currently rather than at the time.
The main issue here (based on what you have written since we don't have code to look at) is that if the console log you have is run before the data call returns, then there won't be any data in the foodList array, so this.state.foodList[0] ===undefined and you can't access a property of undefined.
In react, if you want to console log a state change, a good option is to use the setState method's 2nd callback parameter. That is guaranteed to run after the state change.
callServer() {
fetch("http://localhost:4000/food/")
.then(res => res.json())
.then(res => this.setState({ foodList: res },()=>console.log(this.state.foodList[0].name))
.catch(err => err);
}
If you want to keep the console.log in the render function, you can either check to make sure that the array is actually populated or use the new optional chaining operator (which is supported in the latest create react app versions):
console.log("debug:\n" + JSON.stringify(this.statefoodList[0]?.name))
You try to console.log the first element of the array before the array is populated with the data from your server (as the ajax call takes some tome to execute), so position 0 of this.state.foodList is still undefined.
You can fix this by first checking if the array has a length like this
console.log(this.state.foodList.length && "debug:\n" + JSON.stringify(this.state.foodList[0].name))

Jest testing with Node - Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout

I'm starting to test my code with Jest, and I can't make a seemingly simple test to pass. I am simply trying to check if what I receive from a Maogoose database request is an object.
The function fetchPosts() is working because I hooked it up with a React frontend and it is displaying the data correctly.
This is my function fetchPosts():
module.exports = {
fetchPosts() {
return new Promise((resolve, reject) => {
Posts.find({}).then(posts => {
if (posts) {
resolve(posts)
} else {
reject()
}
})
})
}
}
And my test:
it('should get a list of posts', function() {
return posts.fetchPosts().then(result => {
expect(typeof result).toBe('object')
})
})
This makes the test fail, and Jest says
'Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.'
QUESTION: How can I make this test pass?
You can expect asynchronous results using resolves, as shown in the Jest documentation.
In your case:
it('should get a list of posts', function() {
const result = posts.fetchPosts();
expect(result).resolves.toEqual(expect.any(Object));
})
…although I have a suspicion your list of posts is actually an array, so you probably want this:
it('should get a list of posts', function() {
const result = posts.fetchPosts();
expect(result).resolves.toEqual(expect.any(Array));
})
Another tip: You don't need to wrap the body of your fetchPost in an additional promise, you can simply return the promise you get from Posts.find and add a then to it, like this:
module.exports = {
fetchPosts() {
return Posts.find({}).then(posts => {
if (posts) {
return posts;
}
throw new Error('no posts'); // this will cause a promise rejection
})
}
}
It's also highly possible that you're not getting a response back from the DB at all from your test suite. Test suite's can call different environmental variables / configs that lead to different calls. This error can also be seen if no response is returned, as in - if someone blocks your IP from connecting, on and on.
Also if you are simply looking to increase the timeout, then you can do that by setting
jest.setTimeout(10000);
You can use this statement in beforeEach if you want to change the timeout for all your tests in that describe block or in the test/it/spec block if you want it for a single test.
For me none of the above worked so I tried older version of jest and it worked
npm i -D jest#25.2.7.
if you are using it with typescript make sure to degrade ts-jest as well
npm i -D jest#25.2.7 ts-jest#25.3.1

How to make a firebase cloud onUpdate function to work properly?

I have adding and outputing comments in appliction.
That should work in realy time.
In my Comments component where which is statful component I have entire logic for geting data and saving in the firebase.
Now I want here to add a onUpdate fb cloud function.
I tried on next way and does not work:
const onCommentsAdded = this.runtime.fb.ref('/comments').onUpdate((change) => {
const after = change.after;
console.log('This is after: ', after, change.after)
})
Application cannot be compiled, this is error:
Unhandled Rejection (TypeError): runtime.fb.ref(...).onUpdate is not a
function
Assuming you're looking at the Realtime Database on Firebase and not Firestore.
The library you are trying to access doesn't look correct. See the Database Events documentation.
const onCommentsAdded = functions.database.ref('/comments')
.onUpdate((change, _context) => {
const after = change.after;
console.log('This is after: ', after, change.after)
});
Try updating the function to use functions.database.ref instead of this.runtime.fb.ref

Categories