I'm building a complete pokedex app using react-native/expo with all 900+ Pokémon.
I've tried what seems like countless ways of fetching the data from the API, but it's really slow.
Not sure if it's my code or the sheer amount of data:
export const getAllPokemon = async (offset: number) => {
const data = await fetch(
`https://pokeapi.co/api/v2/pokemon?limit=10&offset=${offset}`
);
const json = await data.json();
const pokemonURLS: PokemonURLS[] = json.results;
const monData: PokemonType[] = await Promise.all(
pokemonURLS.map(async (p, index: number) => {
const response = await fetch(p.url);
const data: PokemonDetails = await response.json();
const speciesResponse = await fetch(data.species.url);
const speciesData: SpeciesInfo = await speciesResponse.json();
return {
name: data.name,
id: data.id,
image: `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/${data.id}.png`,
description: speciesData.flavor_text_entries,
varieties: speciesData.varieties,
color: speciesData.color.name,
types: data.types,
abilities: data.abilities,
};
})
);
Then I'm using it with a useEffect that increases offset by 10 each time and concats the arrays, until offset > 900.
However like I said, it's really slow.
Should I be saving this data locally to speed things up a little?
And how would I go about it? Should I use local storage or save an actual file somewhere in my project folder with the data?
The biggest performance issue I can see is the multiple fetches you perform as you loop though each pokemon.
I'm guessing that the data returned by the two nested fetches (response and speciesResponse) is reference data and are potentially the same for multiple pokemon. If this is the case, and you can't change the api, then two options pop to mind:
Load the reference data only when needed ie. when a user clicks on a pokemon to view details.
or
Get ALL the reference data before the pokemon data and either combine it with your pokemon fetch results or store it locally and reference it as needed. The first way can be achieved using local state - just keep it long enough to merge the relevant data with the pokemon data. The second will need application state like redux or browser storage (see localStorage or indexeddb).
Related
I am using cryptocomare API to get crypto coins data within a Nextjs App. What i doing is that when a user clicks on a perticular symbol, i redirect it to the coin details page where i try to extract the clicked symbol with getServerSideProps as follows and then dynamically put in the API call and send it to the API server.
`
export const getServerSideProps = async (context) => {
const res = await fetch(
`https://min-api.cryptocompare.com/data/pricemultifull?tsyms=USD&fsyms=${context.params.symbol}`
);
const icon = await res.json();
return {
props: {
icon,
},
};
};
`
This call returns a json object of nested objects and it goes to 2-3 levels deep. On Top it looks like following:
API call response
Inside my code, I want to access the data Object -> RAW -> (whatever the user clicked on). But, Since the Symbol or coin queried by the user is dynamic (means i can't predict what is clicked) I never know what to query. SO i tried this to access the data object.RAW[0]
In principal it should give me the whatever object is inside the object.RAW But it returns undefined
Can please someone guide me , how can i get the data inside object.RAW without knowing what is inside?
Thanks!
I have tried object.RAW[0] to access the data...,....
You can use Object.values(object.RAW) to get an array of the values inside RAW (assuming RAW is not undefined)
Doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values
I know there are similar questions to this on stack overflow but thus far none have been able to help me get my code working.
I have a function that takes an id, and makes a call to firebase firestore to get all the documents in a "feedItems" collection. Each document contains two fields, a timestamp and a post ID. The function returns an array with each post object. This part of the code (getFeedItems below) works as expected.
The problem occurs in the next step. Once I have the array of post ID's, I then loop over the array and make a firestore query for each one, to get the actual post information. I know these queries are asynchronous, so I use Promise.all to wait for each promise to resolve before using the final array of post information.
However, I continue to receive "undefined" as a result of these looped queries. Why?
const useUpdateFeed = (uid) => {
const [feed, setFeed] = useState([]);
useEffect(() => {
// getFeedItems returns an array of postIDs, and works as expected
async function getFeedItems(uid) {
const docRef = firestore
.collection("feeds")
.doc(uid)
.collection("feedItems");
const doc = await docRef.get();
const feedItems = [];
doc.forEach((item) => {
feedItems.push({
...item.data(),
id: item.id,
});
});
return feedItems;
}
// getPosts is meant to take the array of post IDs, and return an array of the post objects
async function getPosts(items) {
console.log(items)
const promises = [];
items.forEach((item) => {
const promise = firestore.collection("posts").doc(item.id).get();
promises.push(promise);
});
const posts = [];
await Promise.all(promises).then((results) => {
results.forEach((result) => {
const post = result.data();
console.log(post); // this continues to log as "undefined". Why?
posts.push(post);
});
});
return posts;
}
(async () => {
if (uid) {
const feedItems = await getFeedItems(uid);
const posts = await getPosts(feedItems);
setFeed(posts);
}
})();
}, []);
return feed; // The final result is an array with a single "undefined" element
};
There are few things I have already verified on my own:
My firestore queries work as expected when done one at a time (so there are not any bugs with the query structures themselves).
This is a custom hook for React. I don't think my use of useState/useEffect is having any issue here, and I have tested the implementation of this hook with mock data.
EDIT: A console.log() of items was requested and has been added to the code snippet. I can confirm that the firestore documents that I am trying to access do exist, and have been successfully retrieved when called in individual queries (not in a loop).
Also, for simplicity the collection on Firestore currently only includes one post (with an ID of "ANkRFz2L7WQzA3ehcpDz", which can be seen in the console log output below.
EDIT TWO: To make the output clearer I have pasted it as an image below.
Turns out, this was human error. Looking at the console log output I realised there is a space in front of the document ID. Removing that on the backend made my code work.
Im working on an app that mainly is a large form that contains several dropdowns. These dropdowns should
populate with some data from the DB. The app has a server with Express that it works as a proxy: I call it from the front end
and then that server calls some external API's, format the info and then send them back to the front.
Im thinking on having on the Backend a route like "/dropdown" call that route from the front end. And once I get the data (I would like to receive it like this)
data: {
dataForDropdown1:[data],
dataForDropdown2:[data],
dataForDropdown3:[data],
dataForDropdown4:[data],
...etc
}
store it on Redux and call them with a selector and populate all the dropdowns required.
The problem is on the server. I need to get that info from different APIs so in the route in Express I was thinking on use something like
Promise.all([promisefetchUrl1],[promisefetchUrl2],[promisefetchUrl3],...etc)
but I don't know how to format like the object above, because as I understand Promise.all doesn't have an order it just returns the info with the first promise resolved, then the second, etc so it would be hard to know
which info is which.
I was thinking on using async await but its way too dirty, like this:
const info1 = await fetch(url1)
const info2 = await fetch(url2)
const info3 = await fetch(url3)
const info4 = await fetch(url4)
etc
then return it like this
const data = {
dataForDropdown1:info1.data,
dataForDropdown2:info2.data,
dataForDropdown3:info3.data,
dataForDropdown4:info4.data
...etc
}
any advices?
Promise.all doesn't have an order it just returns the info with the first promise resolved, then the second, etc so it would be hard to know which info is which.
It does have an order - the first element in the resolve array will correspond to the first Promise in the array passed to Promise.all, etc, regardless of the order in which the promises resolve.
Use:
const results = await Promise.all([
getData(promisefetchUrl1),
getData(promisefetchUrl2),
getData(promisefetchUrl3),
getData(promisefetchUrl4), // this could be made less repetitive
]);
const data = {
dataForDropdown1: results[0],
dataForDropdown2: results[1],
dataForDropdown3: results[2],
dataForDropdown4: results[3],
};
where getData takes the URL and returns a Promise that resolves to the data corresponding to the URL.
I have an array of objects which some of the items within it has a "logo" object containing a key of an image stored on a S3 Bucket.
This is an optional field, so sometimes it doesn't exist in the object. To retrieve the image, I am using the Storage api from AWS-Amplify. This Storage function returns me a promise and here is my first problem.
const fetchUsers = async () => {
setLoading(true);
const formatedData = [];
const { data } = await API.graphql(graphqlOperation(listUsers));
console.log({ data });
data.listUsers.items.map(async user => {
if (user.logo) {
console.log(Storage.get());
const image = await Storage.get(user.logo.key);
user.image = image;
formatedData.push(user);
} else {
formatedData.push(user);
}
})
}
While I am waiting the response for the response from the Storage.get function, the loop keep running, which is modifying the original sorting that came from the data array.
This is causing all the entries that has images are appearing at last on the array.
So given this, I assume that
There's a way to prevent the loop to run while I am still waiting for the response
There's a better/more efficient way to get these images from S3.
Any thoughts how to make it run more smoothly ?
I tried it with this method
useEffect(() => {
async function getUserData() {
var userData = await firebase.firestore().doc("ingredients/" + user.uid).get();
var labels = []
userData.data().ingredients.forEach(ingredient => {
labels.push(ingredient.label)
})
setUserIngredients(labels)
setUserRecipes(userData.data().recipes)
}
getUserData()
fetchRecipes(userIngredients)
}, [])
but it does not load when I use the app I need to save the file on my computer before it fetches the data. I'm using expo to debug the app on my smartphone. I want it to fetch the data when I go to the screen or when my app is starting
You can use loader when data is being fetching and hide it when data is fetched !
also if you want instant data fetch even before rendering screen ,then it is not the good approach if you really want it then do data fetch in custom constructor!
i.e
const [constructorHasRun, setConstructorHasRun] = useState(false);
and call fetch API in this constructor
const constructor = () => {
if (constructorHasRun) return;
// fetch API
setConstructorHasRun(true);
};
Hope You wants it and helped !
For these cases, I recommend saving data in Redux states is helping your app seems fast and you can access it when you need it.
Also, if you don't use Redux, you can save your data in SQLite because it seems your data about recipes, and update your data in the splash screen.
That's just another solution...