In the code snippet you see, I am trying to reach the data that I have determined through asyncStorage in the getToken and `` functions, but when I open the page with these codes from the emulator, the data is empty for the first time, and then when I do ctrl+s from the editor, the data is full. What is the reason for this problem?
App.js Page
getToken: async () => {
const token = AsyncStorage.getItem('userToken');
return token;
},
getMail: async () => {
const mail = AsyncStorage.getItem('userMail');
return mail;
},
OrderListScreen Page
getToken().then((res) => {
if(res){
setToken(res);
console.log(token)
}else {
setToken('');
}
});
getMail().then((res) => {
if(res){
setMail(res);
console.log(mail)
}else {
setMail('');
}
});
Apply await before using AsyncStorage.getItem:
getToken: async () => {
const token = await AsyncStorage.getItem('userToken');
return token;
},
getMail: async () => {
const mail = await AsyncStorage.getItem('userMail');
return mail;
},
In the log you'll not get the updated state in next line of state setter.
getToken().then((res) => {
if(res){
setToken(res);
console.log(token); //You'll never get this value here because state updates are asynchronous in React
console.log("res : ", res);
}else {
setToken('');
}
});
getMail().then((res) => {
if(res){
setMail(res);
console.log(mail)//You'll never get this value here because state updates are asynchronous in React
console.log("Email Res : ", res);
}else {
setMail('');
}
});
Related
Looking for the best way to accomplish the following.
The goal is to, when this screen loads:
Retrieve the user's auth token from async storage, THEN
Hit an API with the auth token to grab some user details (in this case userId)
I'm also trying to useEffect so these only run once.
Function 1:
const getUserToken = async () => {
try {
const userToken = await AsyncStorage.getItem("userToken", userToken);
setUserToken(userToken);
} catch (err) {
console.log("err retrieving token " + err);
}
Function 2:
const getUserId = async () => {
try {
let response = await xano.get("/auth/me", {
headers: { Authorization: userToken },
});
setUserId(response.data.id);
console.log(userId);
} catch (err) {
console.log("getUserId err " + err);
console.log(err.data);
}
};
Function 3:
useEffect(() => {
getUserToken();
getUserId();
}, []);
You need to await those async functions in the useEffect hook.
useEffect(async () => {
await getUserToken();
await getUserId();
}, [])
In my header component:
signIn() {
signInWithPopup(auth, provider).then((result) => {
this.updateUser(result.user.uid);
const userRef = doc(db, 'users', result.user.uid);
this.firestoreUser(userRef)
.then((userDoc) => {
if (!userDoc.exists()) {
this.addNewUserToFirestore(userRef, result.user);
}
})
.then(() => {
console.log('Read user from firestore');
// FIXME: readUserFromFirestore still isn't finishing before moving on...
this.readUserFromFirestore();
})
.then(() => {
console.log('Read personal patches');
this.readPersonalPatches();
})
.then(() => {
console.log('Add watcher');
this.geolocationId = navigator.geolocation.watchPosition(
this.nearLandmark,
this.errorCallback
);
});
});
},
readUserFromFirestore:
async readUserFromFirestore({ commit, state }) {
const userRef = doc(db, 'users', state.user);
try {
const userDoc = await getDoc(userRef);
await (() => {
return new Promise((resolve) => {
for (const property in userDoc.data()) {
const propertyValue = userDoc.data()[property];
commit('addProfileProperty', {
propertyName: property,
propertyValue,
});
}
console.log(
'Just finished putting in user patches',
state.profile.patches
);
resolve();
});
})();
} catch (e) {
alert('Error!');
console.error(e);
}
},
};
readPersonalPatches:
async readPersonalPatches({ commit, state }) {
try {
if (state.user) {
// Get a copy of all the user's patches
state.ownedPatchesArray = [];
state.unownedPatchesArray = [];
await (function () {
console.log('Made it inside the await from readpersonalpatches');
return new Promise((resolve) => {
console.log('raw badges', state.rawPatches);
console.log('user badges', state.profile.patches);
state.rawPatches.forEach((patch) => {
if (JSON.stringify(state.profile.patches).includes(patch.slug)) {
commit('addToArray', {
arr: 'ownedPatchesArray',
value: patch,
});
} else {
commit('addToArray', {
arr: 'unownedPatchesArray',
value: patch,
});
}
});
resolve();
});
})();
}
} catch (error) {
alert('Error reading personal patches');
console.log(error);
}
},
Console Output:
Read user from firestore
Read personal patches
Made it inside the await from readpersonalpatches
raw badges **accurate badge list**
user badges undefined
TypeError: Cannot read properties of undefined (reading 'includes')
Add watcher
Just finished putting in user patches **accurate user patch list**
In readUserFromFirestore I wasn't sure exactly how to approach waiting on the user's patches to be added to the array before moving on in the sign-in process. One of the properties that is being looped over is profile.patches. readPersonalPatches() uses that property. But on fresh logins I get an error in readPersonalPatches() because profile.patches is undefined at that point. (On logins after cacheing I do not have an issue reading profile.patches apart from the data potentially being outdated.)
I am using Vue, Vuex, and Firebase for Authentication and Firestore.
For my purposes patch and badge are interchangeable terms.
Thanks to Luaan for educating me on how then blocks work I have it going now. I wasn't returning the promises, only calling the function and then not doing anything with the returned promises 🤦
Fixed lines:
.then((userDoc) => {
return (function () {
if (!userDoc.exists()) {
this.addNewUserToFirestore(userRef, result.user);
}
})();
})
.then(() => {
console.log('Read user from firestore');
return this.readUserFromFirestore();
})
.then(() => {
console.log('Read personal patches');
return this.readPersonalPatches();
})
I have got this Node.JS snippet and would like to write it as a module, so I can use recaptcha in different parts of my system.
This is how it currently looks like:
app.post('/register_user', (req, res) => {
const secret_key = process.env.RECAPTCHA_SECRET;
const token = req.body.recaptcha;
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
fetch(url, { method: "post",})
.then((response) => response.json())
.then((google_response) => {
if (google_response.success == true) {
res.format({'text/html': () => res.redirect(303, '/register'),})
} else {
return res.send({ response: "Failed" });
}
})
.catch((error) => {
return res.json({ error });
});
})
I have tried to write the following module which works absolutely great, but I have absolute no idea about how to call it from the app.post, since I always get undefined as return:
import fetch from 'node-fetch';
export function fetch_out(url, timeout = 7000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), timeout)
)
]);
}
export async function checkRecaptcha(token, secret_key){
const url = "https://www.google.com/recaptcha/api/siteverify?secret=" + secret_key + "&response=" + token;
try{
const response = await fetch_out(url, 1000);
const google_response = await response.json();
}catch(error){
return error;
}
return google_response;
}
Any help would be appreciated! Thanks!
You could make this method reusable by removing the framework actions that need to happen and only return if the validation was successful or not. This way, it will be reusable in another project that doesn't use a specific framework.
Example module;
export async function checkRecaptcha(token, secret_key) {
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
const response = await fetch(url, { method: "post",});
if (!response.ok) return false;
const json = await response.json();
if (!json.success) return false;
return true;
}
Usage:
import { checkRecaptcha } from "./some-file-name";
app.post('/register_user', async (req, res) => {
const isHuman = await checkRecaptcha(req.body.recaptcha, process.env.RECAPTCHA_SECRET);
if (!isHuman) {
return res.send({ response: "Failed" });
}
return res.format({'text/html': () => res.redirect(303, '/register'),});
});
If you specifically want to call an action after the validation, you can also use successful and error callbacks.
I've trying to redirect to home if user is not logged in(status 401) in componentDidMount.
So I tried
componentDidMount() {
const requestPatientListApi = async () => {
try {
const { data } = await get<AxiosResponse<PatientApi>>("/patient", { requester: "admin" });
return data.items;
} catch (err) {
//when user is not logged in redirect to "./"
props.history.push("./");
}
return [];
};
if (!axios.isCancel("")) {
// updating state when api call is not canceled
requestPatientListApi().then(patients => {
setPatients(patients);
});
}
}
componentWillUnmount() {
if (cancel) {
cancel();
}
}
But, the error occurs:
Warning: Can't perform a React state update on an unmounted component.
I've tried using axios cancel token, but it seems not a solution for this case.
Any ideas?
The issue is that you're redirecting before setting a state, in which case the component is not rendered anymore. One way would be to forward the error in your async func and catch it later.
componentDidMount() {
const requestPatientListApi = async () => {
try {
const { data } = await get <AxiosResponse<PatientApi>>("/patient", {
requester: "admin"
});
return data.items;
} catch (err) {
throw err; // Forward the error further
}
return [];
};
if (!axios.isCancel("")) {
requestPatientListApi().then(patients => {
setPatients(patients);
}).catch(err => {
props.history.push("/"); // handle the error here
});
}
}
I have an async await function that handles form submission in React Native:
const handleSubmit = async () => {
const credentials = { email, password }
try {
login(credentials)
} catch {
console.error('ERROR')
//external function to reset form
resetForm()
return
}
// User authenticated, go to home screen
await goToHomeScreen()
}
Where login(), which makes the API call, is
const login = (credentials) => {
axios
.post(`${MY_API}/login`, credentials)
.then((res) => {
console.log(res.data)
})
.catch(() => {
throw 'Error'
})
}
The idea is that if the authentication call fails, my login() throws an error, which should run the return in my handleSubmit's catch {}, ending the function. However, the catch never runs, and goToHomeScreen() runs. What am I doing wrong?
not just return but do something
axios
.get(url)
.then((response) => {
console.log('response', response);
}
.catch((error) => {
console.log('CATCH');
window.alert('CATCH');
}
Try this, it is working for me
const login = (credentials) => {
axios
.post(`${MY_API}/login`, credentials)
.then((res) => {
console.log(res.data)
})
.catch((error ) => {
return Promise.reject(error);
})
}
since you are using async-await, you can call login as:
const handleSubmit = async () => {
const credentials = { email, password }
try {
await login(credentials)
} catch {
console.error('ERROR')
//external function to reset form
resetForm()
return
}
// User authenticated, go to home screen
await goToHomeScreen()
}
const login = async (credentials) => {
return await axios.post(`${MY_API}/login`, credentials);
}
it will solve the issue