I am trying to save the name that a user enters into a TextInput such that every proceeding time a user opens the app their name will still be saved.
I am trying to use react-native's Asynchronous storage to get the name inside the componentDidMount() function. I believe that the value is not being set inside the onSubmit() method when it is being called. I tried removing the await keyword in the async function. I read other the documentation and can't find where I am going wrong.
onSubmit = () => {
var that = this
var data = this.state.textValue;
nameDict['name'] = data;
_setData = async () => {
try {
const value = await AsyncStorage.setItem('name',data)
that.setState({textValue:'',name:data})
} catch (error) {
that.setState({textValue:'',name:'error'})
}
}
}
componentDidMount(){
var that = this;
var name = ''
this.watchId = navigator.geolocation.watchPosition((position)=>{
_retrieveData = async () => {
try {
const value = await AsyncStorage.getItem('name')
name = value
} catch (error) {
// Error retrieving data
}
}
}
The name variable in ComponentDidMount() is always an empty string, so either it has not been changed at all or the getItem() function is not resolving and returning the name. However, I am not sure which of the two it could be. Am I using the Async functions incorrectly?
you have to set set componentDidMount to async also
example using your data:
async componentDidMount(){
var that = this;
var name = ''
this.watchId = navigator.geolocation.watchPosition((position)=>{
_retrieveData = async () => {
try {
await AsyncStorage.getItem('name').then((value)=>name=value);
} catch (error) {
// Error retrieving data
}
}
}
I recommend making name global if you want to actually use the value.
next, this might be personal but i recommend to JSON.stringify() the values when storing data and JSON.parse() when retrieving the data
example when retrieving stringify data
async componentDidMount(){
var that = this;
var name = ''
this.watchId = navigator.geolocation.watchPosition((position)=>{
_retrieveData = async () => {
try {
await AsyncStorage.getItem('name').then((value)=>name=JSON.parse(value));
} catch (error) {
// Error retrieving data
}
}
}
if this was helpful please set as the answer if not please leave a comment.
Related
I am attempting to utilize JavaScript to construct an object that has a property, utilizing a getter method to retrieve data from the database every time it is invoked.
The actual working code is shown below, however, the multi-connection database aspect is not covered. Having done that, I'm not sure how accurate it is, as the resources referenced during the implementation didn't cover this specific aspect. This leads to some confusion about the difference between attributes and behavior
class Article {
constructor(info) {
this._id = info.id;
this._content = info.content;
}
get content() {
return (async () => {
try {
const query = { id: this._id };
const artData = await ArticleModel.findOne(query);
return artData.content;
} catch (err) {
console.log(err);
}
})();
}
......
...utilizing a getter method to retrieve data from the database every time it is invoked
If it's going to do the retrieval every time it's accessed, I would suggest not making it a getter. That's behavior for a method, not an accessor property.
Re the try/catch in your code: In general, allow errors to propagate rather than handling them too early. That try/catch is definitely too early, the calling code should have a chance to know the promise has been rejected, rather than what the code above is doing, which is converting rejection into fulfillment with undefined.
class Article {
constructor(info) {
this._id = info.id;
this._content = info.content;
}
async getContent() {
const query = { id: this._id };
const artData = await ArticleModel.findOne(query);
return artData.content;
}
}
If you were going to make this an accessor property instead, then it shouldn't start a new request every time it's accessed. Instead, save the promise and reuse it:
class Article {
#dataPromise = null;
constructor(info) {
this._id = info.id;
this._content = info.content;
}
get content() {
if (!this.#dataPromise) {
this.#dataPromise = (async () => {
const query = { id: this._id };
const artData = await ArticleModel.findOne(query);
return artData.content;
})();
}
return this.#dataPromise;
}
}
Or equivalently:
class Article {
#dataPromise = null;
constructor(info) {
this._id = info.id;
this._content = info.content;
}
get content() {
if (!this.#dataPromise) {
const query = { id: this._id };
this.#dataPromise = ArticleModel.findOne(query)
.then((artData) => artData.content);
}
return this.#dataPromise;
}
}
But again, I wouldn't use an accessor property for this. I'd use a method.
While I was working on a project, there some case that I have to change a state when user update a data.
Here is the code :
state = {
changedReservationStatus: toJS(this.props.reservationViewModel.getReservation())
}
reloadPageData = async () => {
const { reservationViewModel, reservationOrderViewModel } = this.props
const reservation = toJS(reservationViewModel.getReservation())
await reservationViewModel.fetchReservation(reservation.id).then(changedReservationStatus => this.setState({ changedReservationStatus }))
}
( reservation variable contains a data of an array. )
When user clicks save button, reloadPageData function works and then await reservationViewModel.fetchReservation starts to work, it triggers this.setState after .then statement.
But as you may know reservationViewModel.fetchReservation and this.setState both work asynchronously.
when I console.log this.state.changedReservationStatus inside render function, it renders the data I want to get.
But is it okay to use this.setState inside of Promise?
Will there be any issue?
Try the following solution .
state = {
changedReservationStatus: toJS(this.props.reservationViewModel.getReservation())
}
reloadPageData = async () => {
const { reservationViewModel, reservationOrderViewModel } = this.props
const reservation = toJS(reservationViewModel.getReservation())
await changedReservationStatus = reservationViewModel.fetchReservation(reservation.id);
this.setState({ changedReservationStatus });
}
I am trying to call a function in my action, but get the error this.setToken is not a function:
async function setToken() {
const {
credentials: { year, group, student }
} = this.props;
const fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
firebase
.firestore()
.collection("users")
.doc(fcmToken)
.set({
year
})
.then(function() {
return true;
})
.catch(function(error) {
return false;
});
}
}
export function fetchEvents(id) {
const currentDateString =
moment().format("YYYY-MM-DD") + "T" + "07:00:00.000Z";
const url = xxx;
return async dispatch => {
dispatch(isLoading(true));
const setToken = await setToken(); // call here
fetch(url)
.then(response => {
return response;
})
.then(response => response.json())
.then(data => {
const { error } = data;
if (error) {
dispatch(hasErrored(error.message));
} else {
dispatch(fetchSuccessEvents(data.items));
}
navigate("Month");
dispatch(isLoading(false));
});
};
}
Any ideas?
setToken is defined as a standalone function, not a property of an instance, or of the current this, or anything like that:
async function setToken() {
So, call it like you would call any other function, without putting this in front of it.
You also cannot use the same variable name in the inner scope, else you'll refer to the inner variable (which will start out as undefined / uninitialized); assign the result to a different variable name instead:
const token = await setToken();
But your current code doesn't have setToken resolve to anything, in which case, you may simply await it, without assigning the result to anything:
await setToken();
Use
setToken()
instead of
this.setToken()
You are not in a class environment, as far as I can tell, so setToken() is not an instance or a property, but a standalone function.
If you prefer to use a class notation, you could use window.setToken().
I have an object of convos with userIDs that I need to loop through and, inside the loop, I need to make a call to Firebase to get the corresponding userName and then return an object with the convos, userNames, and userIDs.
I have tried using the async/await and the result I get from console.log is correct but my return statement directly after that, is undefined. Why is this happening? They are receiving the same object.
store.js getter snippet
getConvosObj: state => {
var convoObj = {};
var userConvos = state.userProfile.convos;
async function asyncFunction() {
for (const key in userConvos) {
if (userConvos.hasOwnProperty(key)) {
const userID = userConvos[key];
var userName;
await fire.database().ref('/users/' + userID + '/userName').once('value', async (snapshot) => {
userName = await snapshot.val();
convoObj[key] = {userName, userID}
})
}
}
console.log(convoObj); //result: correct object
return convoObj; //result: undefined
}
asyncFunction();
}
Why is this happening ?
Because you called async function synchronously.
lets make your code simpler.
getConvosObj: state => {
async function asyncFunction() {
// ...
}
asyncFunction();
}
at this point, your getConvosObj() will return nothing, because getConvosObj() ends before asyncFunction() ends.
you need to wait until your asyncFunction() ends, then your code should be like this:
getConvosObj: async state => { // <- changed here
async function asyncFunction() {
// ...
}
await asyncFunction(); // <- changed here too
}
but you should not do like this, because getters are not meant to be asynchronous by design.
this may work, but you should try a different approach.
So what should you do ?
Use actions before using getters
this is a basic approach.
async functions should be in actions.
so your store should be like this:
export default () =>
new Vuex.Store({
state: {
convoObj: null
},
mutations: {
updateConvoObj(state, payload) {
state.convoObj = payload;
}
},
actions: {
async fetchAndUpdateConvoObj({ state, commit }) {
const fetchUserData = async userId => {
const snapShot = await fire.database().ref('/users/' + userID + '/userName').once('value');
const userName = snapShot.val();
return {
userName: userName,
userID: userId
}
}
const userConvos = state.userProfile.convos;
let convoObj = {};
for (const key in userConvos) {
if (userConvos.hasOwnProperty(key)) {
const userId = userConvos[key];
const result = await fetchUserData(userId);
convoObj[key] = {
userName: result.userName,
userId: result.userId
}
}
}
commit('updateConvoObj', convoObj);
}
}
});
then call your actions before using getter in your sample.vue:
await this.$store.dispatch('fetchAndUpdateConvoObj');
convoObj = this.$store.getters('getConvoObj');
wait for db and update store, then get its state.
doesnt make sense ?
Use vuexfire to connect your store directly to Realtime Database
another approach is this.
use vuexfire, then the state of the store is always up-to-date to realtime database, so you can call getters without calling actions.
i got tired to refactor / write a code, so google some sample if you wanna use that plugin :)
i refactored the original code a lot, so there should be some typo or mistake.
plz revise is if you find one.
I am trying to understand async calls using async/await and try/catch.
In the example below, how can I save my successful response to a variable that can be utilized throughout the rest of the code?
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample();
console.log(globalData) //Promise { <pending> }
1) Return something from your asyncExample function
const asyncExample = async () => {
const result = await axios(users)
return result
}
2) Call that function and handle its returned Promise:
;(async () => {
const users = await asyncExample()
console.log(users)
})()
Here's why should you handle it like this:
You can't do top-level await (there's a proposal for it though);
await must exist within an async function.
However I must point out that your original example doesn't need async/await
at all; Since axios already returns a Promise you can simply do:
const asyncExample = () => {
return axios(users)
}
const users = await asyncExample()
try..catch creates a new block scope. Use let to define data before try..catch instead of const, return data from asyncExample function call
(async() => {
const users = 123;
const asyncExample = async() => {
let data;
try {
data = await Promise.resolve(users);
} catch (err) {
console.log(err);
}
return data;
};
//Save response on a variable
const globalData = await asyncExample();
console.log(globalData);
// return globalData;
})();
I had same issue with you and found this post. After 2 days of trying I finally found a simple solution.
According to the document of JS, an async function will only return a Promise object instead of value. To access the response of Promise, you have to use .then()method or await which can return the resulting object of Promise is instead of Promise itself.
To change variables from await, you have access and change the variable you want to assign within the async function instead of return from it.
//Save response on a variable
var globalData;
const asyncExample = async () =>{
try {
const data = await axios(users);
globalData = data; // this will change globalData
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
asyncExample();
But if you do this, you may get an undefined output.
asyncExample();
console.log(globalData) //undefined
Since asyncExample() is an async function, when console.log is called, asyncExample() has not finished yet, so globalData is still not assigned. The following code will call console.log after asyncExample() was done.
const show = async () => {
await asyncExample();
console.log(globalData);
}
show();
Because the events are happening asynchronously you need to tie in a callback/promise. I'm going to assume it returns a promise.
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample().then( (success, err) => {
if (err) { console.error(err); }
console.log(success)
}
Just use a callback/promise (cascading programming):
axios(users).then(function(response) {
const globalData = response;
console.log(globalData)
});