I have a code that will save user in the database on my profile screen, but the code worked fine on iOS, but on android it throws a "Possible unhandled Promise Rejection" with "TypeError: Symbol.asyncIterator is not defined."
Please how can solve the issue on Android so that it can save to database? I am working with React Native.
Here is the code snippet:
import {Auth, DataStore} from 'aws-amplify';
const ProfileScreen = () => {
const [name, setName] = useState('');
const [bio, setBio] = useState('');
const [gender, setGender] = useState();
const [lookingfor, setLookingfor] = useState();
const isValid = () => {
return name && bio && gender && lookingfor;
};
const save = () => {
if(!isValid()) {
console.warn('Not valid');
return;
}
const newUser = new User({
name,
bio,
gender,
lookingfor,
image: 'https://notjustdev-dummy.s3.us-east-2.amazonaws.com/avatars/elon.png',
});
DataStore.save(newUser);
};
return ...
I've been doing the same project. It worked for me when I did:
npm install #azure/core-asynciterator-polyfill
Then added:
import '#azure/core-asynciterator-polyfill';
at the very top of my imports in the page I was working on.
Accoding to the documentation, since save returns a promise instead of doing the opeartion itself, add await or subscribe with then to the save function;
await DataStore.save(newUser);
or
DataStore.save(newUser).then(...);
Related
I am trying to create a React Native app with Expo and authenticate using google. I am following this documentation. I expect response.authentication.accessToken to be a string access token, instead it is null. My error message is Typeerror: null is not an object (evaluating 'response.authentication.accesstoken')
Here is the relevant part of my code:
WebBrowser.maybeCompleteAuthSession();
export default function App() {
const [accessToken, setAccessToken] = React.useState(null);
const [user, setUser] = React.useState(null);
const [request, response, promptAsync] = Google.useIdTokenAuthRequest({
expoClientId: "[REDACTED].apps.googleusercontent.com",
webClientId: "[REDACTED].apps.googleusercontent.com",
iosClientId: "[REDACTED].apps.googleusercontent.com",
androidClientId: "[REDACTED].apps.googleusercontent.com",
});
React.useEffect(() => {
if(response?.type === "success") {
console.log(response);
setAccessToken(response.authentication.accessToken);
accessToken && fetchUserInfo();
}
}, [response, accessToken])
Why is it null?
The solution I found was to switch functions, Google.useIdTokenAuthRequest to Google.useAuthRequest. I also fixed my bundleIdentifier and android package in app.json to exactly match my slug (there were capitalization issues).
Here is the new script with the find function which allows me to identify a single element of the array for sure but there is still a small problem. As you can see it's my const crypto which contains the data I want to display on the front end. However when I want to call crypto at the return level the const is not recognized.
Hello again,
I updated the script now it works I can display on the front end the data I want however I have the impression that the request to the api is executed several times when I would like there to be only one request
I put below a screen of the console.log of my script.
As you can see the data requested is displayed first as undefined then it is displayed several times, then I put blocked due to the too large number of requests made in little time
Thanks for your help
How do I make my requests :
import { useState, useEffect } from "react";
function App() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null)
useEffect(() => {
fetch("http://localhost:4500/coingeckotest")
.then(response => {
if (response.ok) {
return response.json()
}
throw response;
})
.then(data => {
setData(data)
})
.catch(error => {
console.error("Error fetching data: ", error)
setError(error)
})
.finally(() => {
setLoading(false)
})
}, [])
const uniswap = data&&data[0].tickers?.find(donne =>
donne.trade_url === 'https://app.uniswap.org/#/swap?
inputCurrency=0x2260fac5e5542a773aa44fbcfedf7c193bc2c599&
outputCurrency=ETH')
const uniprice = uniswap?.converted_last?.usd
const sushiswap = data&&data[1].tickers?.find(donne =>
donne.trade_url === 'https://app.sushi.com/swap?
inputCurrency=0x2260fac5e5542a773aa44fbcfedf7c193bc2c59
9&outputCurrency=ETH')
const sushiprice = sushiswap?.converted_last?.usd
const curvefinance = data&&data[2].tickers?.find(donne =>
donne.base === 'DAI')
const curveprice = curvefinance?.converted_last?.usd
const quickswap = data&&data[3].tickers?.find(donne =>
donne.trade_url === 'https://quickswap.exchange/#/swap?
inputCurrency=0x0d500b1d8e8ef31e21c99d1db9a6444d3adf127
0&outputCurrency=0xbbba073c31bf03b8acf7c28ef0738decf369
5683')
const quickprice = quickswap?.converted_last?.usd
console.log(uniprice)
console.log(sushiprice)
console.log(curveprice)
console.log(quickprice)
if (loading) return "Loading..."
if(error) return "Error !"
return (
<>
</>
)
}
export default App;
Thank you in advance for your answers
You can use the Array.find method to find out the first entry that matches a particular coin_id. The code for that would be:
const desiredCoinID = "something"; (determined by some user input)
const desiredCoinObject = data.find(coin => coin.coin_id === desiredCoinID);
const priceInUSD = desiredCoinObject?.tickers?.converted_last?.usd;
——-Updated Answer——-
Hi, this is my answer to your updated question. const crypto that you use is available only within the scope of the callback of the useEffect function, which is why you cannot see the results on your screen. There are two ways you can go about it:
First, you can declare crypto as a let outside the useEffect and then update it inside your useEffect. That way your crypto will have global scope. But there is a better way to do this, which is to use the useState hook.
You can declare a crypto, setCrypto using useState and then use the setter to update the value if crypto inside useEffect after the data fetching is over. Let me know if you need help writing code.
I have on my site an page where I handled File upload, but since I upgraded Firebase (I guess it was Firebase v7/8) this particular feature is not working anymore.
To handled the file upload in firebase storage I created a custom hook, where I use the useEffect because I need it to run each time there is a new file value. I passed a parametter (file) for the file I'm trying to upload and store it in database, and that way databse contains all image's url. Then I used the datas to load images in a react components.
The error I've got:
Uncaught TypeError: projectStorage.ref is not a function
Since I'm on Firebase v9 I'm lillte bit confused about it, and don't know what to change. Thank you for your help, I really appreciate =).
useStorage.jsx (custom hook)
import {projectStorage, projectFirestore, timestamp} from '../Firebase'
import { useEffect, useState } from 'react'
function useStorage(file) {
const [progress, setProgress] = useState(0);
const [error, setError] = useState(null);
const [url, setUrl] = useState(null);
useEffect(() => {
const storageRef = projectStorage.ref(file.name)
const collectionRef = projectFirestore.collection('images');
storageRef.put(file).on('state_changed', (snap) => {
let percent = (snap.bytesTransferred / snap.totalBytes) * 100;
setProgress(percent >> 0); // or Math.trunc()
}, (err) => {
setError(err);
}, async () =>{
const url = await storageRef.getDownloadURL();
const createdAt = timestamp();
collectionRef.add({ url, createdAt});
setUrl(url);
});
}, [file]);
return {progress, url, error};
}
export default useStorage;
There's a top level function uploadBytesResumable() to upload files while monitoring progress. Try refactoring the code as shown below:
import {projectStorage, projectFirestore, timestamp} from '../Firebase'
import { ref as storageRef, uploadBytesResumable } from "firebase/storage";
useEffect(() => {
// Creating a storage reference
const storageReference = storageRef(projectStorage, file.name);
// Creating an upload task
const uploadTask = uploadBytesResumable(storageReference, file);
// Monitoring upload progress
uploadTask.on("state_changed", (snapshot: any) => {
console.log(snapshot);
// render progress
});
}, [file])
Checkout the documentation on Upload files with Cloud Storage on Web.
I am implementing a payment system with Stripe extension for firebase in a react native. However, i do not know how to behave in the following situation:
I write initial information for the checkout_session when the user wants to proceed to checkout:
const initializeCheckout = () => {
//write initial payment data
const writePaymentDetails = async () => {
await setDoc(doc(getFirestore(), 'customers', getAuth().currentUser.uid, 'checkout_sessions', getAuth().currentUser.uid),{
client: 'mobile',
mode: 'payment',
amount: subTotal,
currency: 'chf',
});
}
writePaymentDetails();
navigation.navigate('Checkout');
}
After that, a stripe extension in firebase adds all the additional information (ephemeral keys, stripe customer key etc.) to the checkout_session document.
After additional data is written, i want to navigate to the checkout page and then initialize and open paymentSheet in react native as it is indicated in the official stripe tutorial
The checkout screen i implemented:
export default function CheckoutScreen() {
const { initPaymentSheet, presentPaymentSheet } = useStripe();
const [loading, setLoading] = useState(false);
const fetchPaymentSheetParams = async () => {
console.log('still works after calling fetchPaymentSheetParams');
const checkoutSessionDoc = await getDoc(doc(getFirestore(), 'customers', getAuth().currentUser.uid, 'checkout_sessions', getAuth().currentUser.uid));
const paymentIntent = checkoutSessionDoc.data().paymentIntentClientSecret;
const ephemeralKey = checkoutSessionDoc.data().ephemeralKeySecret;
const customer = checkoutSessionDoc.data().customer;
console.log(paymentIntent, ephemeralKey, customer);
return{
paymentIntent: paymentIntent,
ephemeralKey,
customer,
};
};
const initializePaymentSheet = async () => {
const {
paymentIntent,
ephemeralKey,
customer,
} = await fetchPaymentSheetParams();
const { error } = await initPaymentSheet({
customerId: customer,
customerEphemeralKeySecret: ephemeralKey,
paymentIntentClientSecret: paymentIntent,
allowsDelayedPaymentMethods: false,
});
if (!error) {
setLoading(true);
}
};
const openPaymentSheet = async () => {
const { error } = await presentPaymentSheet();
if (error) {
Alert.alert(`Error code: ${error.code}`, error.message);
} else {
Alert.alert('Success', 'Your order is confirmed!');
}
};
useEffect(() => {
console.log('Payment sheet is being initialized');
initializePaymentSheet();
}, []);
return (
<View style={{flex: 1, justifyContent: 'center'}}>
<Button
disabled={loading}
title="Checkout"
onPress={openPaymentSheet}
/>
</View>
);
}
However, i don't know how to wait until the firebase function ends in step 2 before moving to the next step. Now, if i navigate to the checkout screen just after writing the initial data and try to read an ephemeral key, stripe customer key and payment intent, they are undefined.
So, my question is how to make the transition correctly so that the additional information is not undefined?
i have solved this in a cumbersome(most probably) way:
I have defined a snapshot listener that checks if the fields customer, customerEphemeralKeySecret and paymentIntentClientSecret are defined and only then starts initializing the payment sheet.
I am trying to get useremail from localstorage, but the email is not being updated instantly.
Solutions i have tried -
1. Using email in dependency array, this updates the email but people is not re-rendered in DOM
2. Using both email and people in dependency array, which is causing infinite calling of useEffect.
3.I have tried it without promise, directly in sequential flow which is also not updating email.
Please suggest the correct way of handling this.
const [people,setPeople]=useState([])
const [email,setEmail] = useState('')
useEffect(()=>{
new Promise((res,rej)=>{
setEmail(localStorage.getItem('userid')) //here is issue
if(email) res();
else rej(email);
}).then(
fire.firestore()
.collection('Users').where('Email','==',email)
.get().then((snapshot)=>{
console.log(snapshot)
setPeople(snapshot.docs[0].data().Name)
})
.catch(e=>{console.log(e)})
)
.catch((e)=>{console.log(e)})
},[email,people])
You can separate function getting email and people. I assume you want to get people only when having an email.
Getting email after component initially rendered
Then create useEffect for fire.firestore()... dependencies is email
const [people,setPeople]=useState([])
const [email,setEmail] = useState('')
useEffect(()=>{
setEmail(localStorage.getItem('userid'))
},[])
useEffect(()=>{
if(email){
fire.firestore()
.collection('Users').where('Email','==',email)
.get().then((snapshot)=>{
console.log(snapshot)
setPeople(snapshot.docs[0].data().Name)
})
.catch(e=>{console.log(e)})
}
},[email])
Wrapping the localStorage call in Promise makes it complicated. You need to make the firestore call when the email changes. Below are two solutions I can suggest.
The first one only runs when the component mounts and eliminates the email from state.
const [people, setPeople] = useState([]);
useEffect(() => {
// you might need to wrap this in a try catch as it may fail if a user has disabled access to localStorag
const email = localStorage.getItem("userid");
if (email) {
fire
.firestore()
.collection("Users")
.where("Email", "==", email)
.get()
.then((snapshot) => {
console.log(snapshot);
setPeople(snapshot.docs[0].data().Name);
})
.catch((e) => {
console.log(e);
});
}
}, []);
If you want the fire store effect to run whenever the email changes.
You can seperate the two effects and make the firestore effect depend on email changes as shown below.
const [people, setPeople] = useState([]);
const [email, setEmail] = useState('');
useEffect(() => {
const localStorageEmail = localStorage.getItem("userid");
if (email) {
setEmail(localStorageEmail);
}
}); // dependence array ignored will run everytime the component rerenders
useEffect(() => {
if (!email) return;
fire
.firestore()
.collection("Users")
.where("Email", "==", email)
.get()
.then((snapshot) => {
console.log(snapshot);
setPeople(snapshot.docs[0].data().Name);
})
.catch((e) => {
console.log(e);
});
}, [email]);