Call an async function with javascript - javascript

I am trying to call an async function but I am getting an error
getUsersList(db).then is not a function
this is my code
async function getUsersList(db) {
const userCol = collection(db, 'Users');
const userSnapshot = await getDocs(userCol);
const tempUserList = userSnapshot.docs.map(doc => doc.data());
return tempUserList;
}
function App() {
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const auth = getAuth(app);
var currentUser = auth.currentUser;
if(currentUser != null){
getUsersList(db).then((value) => {
console.log(value);
});
I also tried using await getUsersList but got the following error
Unexpected reserved word 'await'

So what I assume you're trying to do here const userSnapshot = await getDocs(userCol); is fetch some data that is then going to be used in your react component to render something (Maybe there's a fetch request in getDocs ?)
There is no return in your react component (but that's not what's causing your issue).
As it is it won't work using await since App() isn't an async function BUT you can't make it an async function since this is a standard react component.
What do you want to happen whilst waiting for the data to be fetched (= whilst your promise is pending) ? If you're happy to display nothing, why not just remove await before getDocs() ?
For more on this topic :
React: async and await not working with fetch
React Hooks: how to wait for the data to be fetched before rendering

Related

Why is this catch all API route not working?

SO I been learning NextJS API routes and NodeJS recently, While I do know how to create dynamic routes now, I have some issues with a few things -
This is my api/matches.js file.
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export default async function handler(req, res) {
const response = await fetch(`https://exampleapi.com/?&token=# `)
const jsonData = await response.json();
res.status(200).json(jsonData);
}
Now, I have another dynamic route for this API which fetches the match by a match slug, So this file was called /api/matches/[...matchslug].js
export default async function handler(req, res) {
const page = req.query.matchslug
const response = await fetch(`https://examleapi.com/?search[slug]=${page}&token=# `)
const jsonData = await response.json();
While this dynamic route fetches the result of one so if I went matches/exampelmatch, I do get the results for the examplematch, However I'm looking to somehow implement it in a way that
matches/examplematch1/examplematch2
Returns the data from the examplematch1 & examplematch2.
I'm not sure if building something like is possible, But very interested.
Thank you for your time and patience.
In your /api/matches/[...matchslug].js example, the value of matchslug will always be an array.
Instead of passing the page variable directly into fetch, you can map over the values in the matchslug array and use Promise.all to request each resource.
It would look something like this (I haven't tested this):
export default async function handler(req, res) {
const promiseArray = req.query.matchslug.map(async (slug) => {
const response = await fetch(`https://examleapi.com/?search[slug]=${slug}&token=# `)
const jsonData = await response.json();
return jsonData
})
const result = await Promise.all(promiseArray)
res.status(200).json(result);
}

Error handling API fetch request with React

I'm using React, and just wanted some advice on error handling.
I have my fetch request in an async function, this function is in another folder and is being imported in my App.js file. Im doing this because I want to try out testing with mock service worker, and have read its easier with the requests in a separate file.
From looking at my code below, is this best practice for error handling? Is there a better way thats more concise?
Here is my fetch async function, at the moment i've purposely gave the wrong env variable name so it will give me a 401 unauthorised error.
require('dotenv').config()
export const collect = async () => {
const key = process.env.REACT_APP_API_KE
try{
const res = await fetch(`http://api.openweathermap.org/data/2.5/weather?q=london&appid=${key}`)
if(res.status !== 200){
throw new Error(res.status)
}
const data = await res.json()
return data
} catch (error){
let err = {
error: true,
status: error.message,
}
return err
}
}
This is being called in my App.js file (not rendering much at the moment)
import { useState } from 'react'
import { collect } from './utilities/collect'
require('dotenv').config()
function App() {
const [data, setData] = useState("")
const [error, setError] = useState({ error: false, status: "" })
const handleFetch = async () => {
let newData = await collect()
if(newData.error){
setError({ error: newData.error, status: newData.status })
}else {
setData(newData)
}
}
return (
<div className="App">
<h1>weather</h1>
<button onClick={handleFetch}>fetch</button>
</div>
);
}
export default App;
Any help or advise would be great.
When writing an abstraction around Promises or async and await one should make sure it is used appropriately, that is a good Promse must allow it consumer to use it then and catch method or should allow it consumer use try and catch to consume it and provide appropriate information on Errors
From Your code, The abstraction doesnt gives back an appropriate response and doesnt follow the standard behavior of a promise it always resolve and never reject and though the code works its implementation of the collect is different from a standard Promise and wont be nice for a standard code base, for example a good abstraction will provide error information returned from the third party api
Appropriate way to amend code
The third party api returns this response
{
"cod":401,
"message": "Invalid API key. Please see http://openweathermap.org/faq#error401 for more info."}
This should be your implementation
// Your fetch functon abstraction modified
require('dotenv').config()
const collect = async () => {
const key = process.env.REACT_APP_API_KE;
const res = await fetch(
`http://api.openweathermap.org/data/2.5/weather?q=london&appid=${key}`,
);
if (res.status !== 200) {
const error = await res.json();
throw {message: error.message,status:error.cod};
}
const data = await res.json();
return data;
};
Your app component should now be like this
import { useState } from 'react'
import { collect } from './utilities/collect'
require('dotenv').config()
function App() {
const [data, setData] = useState("")
const [error, setError] = useState({ error: false, status: "" })
const handleFetch = async () => {
try {
let newData = await collect()
setData(newData)
} catch(e){
setError({ error: e.message, status: e.status })
}
}
return (
<div className="App">
<h1>weather</h1>
<button onClick={handleFetch}>fetch</button>
</div>
);
}
export default App;

acync await react/redux js async events

https://ibb.co/dsNrnPQ -- screenshot of error
I get a problem with an async event.
When logging into the site, when locale-storage is not ready yet. I can't get it async.
After refreshing the page, the problem goes away.
Unhandled Rejection (SyntaxError): Unexpected token u in JSON at position 0
Problem in string
const userData = await userDataGetter();
export function setBalanseFetch(){
return async dispatch => {
const userData = await userDataGetter();
const userID = await userData.userId;
try{
const respone = await axios.post('/api/profile', {userID})
const json = await respone["data"];
const FetchBalanse = json.items[0].balanse;
dispatch( {type: FETCH_BALANSE, payload: Number(FetchBalanse)})
}catch(e){
console.log(`Axios spend balanse request failed: ${e}`);
}
}
}
code function userDataGetter
async function userDataGetter(){
const userData = await JSON.parse(localStorage.userData);
return userData;
}
export default userDataGetter
You have:
const userData = await JSON.parse(localStorage.userData);
However, JSON.parse is not an asynchronous function. It does not wait for a localStorage key to be present and ready, which is what your code seems to want it to do. (Also, as a comment pointed out, you want localStorage.get('userData')). Nor are you checking that it is present at all.
You also don't show where the localStorage is getting set. But likely your solution will be to then trigger the code that depends on it after you know its been set from the same place that's setting it, and when you need to access it any other time, check for its presence first.

React Context outside a React Component or alternatives

I have different endpoints defined in a file called endpoints.ts. Each of those endpoints receive a token (which is used later in Authentication header).
endpoints.ts
const getAllObjects = (token) => fetch(...);
const getSingleObject = (id, token) => fetch(...);
The problem
Every time I use call one of those endpoints from a React component, I have to pass the token (which is saved in React context) like this:
const user = await getSingleObject(id, context.user.token);
I find that very repetitive since I have multiple requests through my application.
What I have tried so far
I tried to simplify endpoints.ts by making it have access to React Context. I wanted to do something like this:
const context = useContext(context);
const token = context.user.token
const getAllObjects = () => fetch(...);
const getSingleObject = (id) => fetch(...);
However, that is not allowed because edpoints.ts does not have a React component so, it is not a valid Consumer for React Context.
Can you suggest another solution?
Rewrite your endpoints.ts to a custom hook using useContext:
const getAllObjects = () => fetch();
const useFetchFunctions = () => {
const context = useContext(context);
const token = context.user.token;
// Can be memoized by token value using React.useCallback
const getSingleObject = (id) => fetch(id, token);
return { getSingleObject, getAllObjects };
};
And use it within context consumers (so useContext will be valid).

Async and await callout to firebase is not waiting

I have a dating-like app and I want to be able to query for new matches every time a user clicks on the "connection" tab button.
I am not sure if I am writing the await or async incorrectly but if the user moves too fast for the database to return the results, the returned matches are not loaded fast enough. What I have so far is: on the load of the page I callout to Firebase, when the user navigates away and then navigates back to the "connection" tab, I call back out to Firebase. the getMatches() method is the callout to firebase.
const MatchesScreen = ({navigation}) => {
const {state, updateDislikedQueue, updateLikedQueue, getMatches} = useContext(AuthContext);
const [loaded, setLoaded] = useState(false);
const [queue, setQueue] = useState({});
const [noMatches, setNoMatches] = useState(false);
const [updateProfileAndPreferences,setUpdateProfileAndPreferences] = useState(false);
const getMatchesMethod = async () => {
getMatches().then(matches => {
if (!matches) {
Alert.alert("Update Preferences and Profile before connecting");
setUpdateProfileAndPreferences(true);
} else {
setUpdateProfileAndPreferences(false);
let cardData = [];
for (m in matches) {
if (matches[m].id == state.id) {
continue;
} else {
let user = {
id: matches[m].id,
fullName: matches[m].info.fullName
};
cardData.push(user);
}
}
if (cardData.length > 0) {
setQueue(cardData);
setLoaded(true);
} else {
setNoMatches(true);
Alert.alert("No Connections Available");
}
}
});
};
useEffect(() => {
getMatchesMethod();
const unsubcribe = navigation.addListener("willFocus", () => {
getMatchesMethod();
});
// return unsubcribe.remove();
}, []);
Also when I try to unsubscribe, the listener doesn't appear to work when the user navigates back and forth. Any help on what I am doing wrong with the async calls and the listener would be greatly appreciated.
I think you just forgot the await keyword in your function
Happens to me all the time ahaha
I recommend using the await keyword in front of getMatches() instead of the .then() syntax. It makes the code read more synchronously, and it can help prevent errors. Await must always be called from an asynchronous function. It cannot be called from a non async function.
I think you will also need to use an await in front of getMatchesMethod();
Checkout this article for help with calling asynchronous code inside an useEffect()
https://medium.com/javascript-in-plain-english/how-to-use-async-function-in-react-hook-useeffect-typescript-js-6204a788a435
Ex:
const unsubcribe = navigation.addListener("willFocus", async () => {
await getMatchesMethod();
});
I found the issue. I was on V3 of react-navigation which is severely outdated was was conflicting with my react-native and expo versions.
I am in the process of updating everything (RN > .60, EXPO sdk > 35, #react-navigation V5) and will be able to use the listeners for #react-navigation v5 on my getMatches() method.

Categories

Resources