Si alguien esta usando un dispositivo Android y no ve que se renderice el ActivityIndicator, es porque por defecto no tiene color y simplemente debe agregarselo.
Documentación
Excelente aporte, no me estaba funcionando hasta que tu publicastes tremendo dato, gracias.
Hola comunidad!
Me di cuenta de un pequeño bug que ocurría, cuando hacia scroll y aparecia el spinner, me cargaba la lista de los siguientes 20 items pero pasaba unos segundos y cargaba nuevamente otros 20 sobre la misma lista lo que ocasionaba que a la lista le falten algunos datos. Esto ocurre porque la única validación que tenemos para ejecutar nuevamente el request es que exista "isNext" en el componente "PokemonList", y ya que desde la segunda llamada existe, cuando el usuario siga haciendo scroll sin que el request haya terminado (sin que desaparezca el spinner), volverá a ejecutarse y a popular la lista nuevamente sobre los datos ya cargados. Para solucionarlo únicamente agregué un state más que le llamé "loading", de manera que solo se haga el request cuando se haya completado el anterior. Mis componentes quedaron de esta manera
import{SafeAreaView}from'react-native';importReact,{useCallback, useEffect, useState}from'react';import{getPokemonDetailsByUrlApi, getPokemonsApi}from'../api/pokemon';importPokemonListfrom'../components/PokemonList';constPokedex=()=>{const[pokemons, setPokemons]=useState([]);const[nextUrl, setNextUrl]=useState(null);const[loading, setLoading]=useState(false);useEffect(()=>{loadPokemons();},[loadPokemons]);//TODO hacer hook useService o useRequest para tener un error y un loadingconst loadPokemons =useCallback(async()=>{try{// inicializamos la "carga" del requestsetLoading(true);const{results: pokemonsResponse,next: nextPokemonListUrl}=awaitgetPokemonsApi(nextUrl);setNextUrl(nextPokemonListUrl);const pokemonsArray =[];forawait(const pokemon of pokemonsResponse){const pokemonDetails =awaitgetPokemonDetailsByUrlApi(pokemon.url); pokemonsArray.push({id: pokemonDetails.id,name: pokemonDetails.name,type: pokemonDetails.types[0].type.name,order: pokemonDetails.id,image: pokemonDetails.sprites.other['official-artwork'].front_default,});}setPokemons([...pokemons,...pokemonsArray]);}catch(error){console.error(error);}finally{// regresamos loading a falsesetLoading(false);}},[pokemons, nextUrl]);return(<SafeAreaView><PokemonList pokemons={pokemons} loadPokemons={loadPokemons} isNext={nextUrl} isLoading={loading}/></SafeAreaView>);};exportdefaultPokedex;
Espero que les sea útil 🤗
Excelente aporte, también noté el bug… por suerte siempre hay un buen compañero que lo soluciona! jaja
Hello Effren!
Muchas gracias por tu aporte, me ayudaste mucho!!
Tengo unas preguntas:
Este fragmento... còmo asi que se llama loadPokemons() cuando load pokemons cambias? o estoy entendindo mal...
useEffect(()=>{loadPokemons();},[loadPokemons]);
Este fragment... para que usas el useCallback?
const loadPokemons =useCallback(async()=>{
Alaaa, pense que seria mas dificil como el la web, que tienes que usar el InsersectionObserver, supongo que aqui tambien lo usa, pero ya underthehood.
Analizando el error de que se brinca los primeros 20 después de unos segundos, opte por ponerlo después de la captura de los pokemons para evitar que se actualizara antes de mostrarlos les dejo el código y cualquier retroalimentación es bienvenida, ```js
const loadPokemons = async () =>
{
try{
const response = await getPokemonsApi(nextUrl)//Se trae la informacion de la api
const pokemonsArray = [];
for await (const pokemon of response.results)
{
//console.log(pokemon)
const pokemonDetails = await getPokemonDetailsByUrlApi(pokemon.url)
pokemonsArray.push(
{id: pokemonDetails.id,
name: pokemonDetails.name,
type: pokemonDetails.types[0].type.name,
order: pokemonDetails.order,
imagen:pokemonDetails.sprites.other['official-artwork'].front_default
})
}
setpokemons([...pokemons,...pokemonsArray])//te genera una sola lista de los elementos de ambas listas
setNextUrl(response.next)
}catch (error){
console.error(error)
}
}
eres un crack!
El error por el cual se salta los primeros 20, es que como primero se realiza la renderización del componente (El useEffect ocurre en un lugar del life cycle conocido como Componentdidmount) y luego se llama automaticamente la función de intersección con el treshold, la cual es recargar los pokemon debido a que el estado donde se guardan los pokemon esta vacio, por tanto la lista y la intersección se esta dando.
Para evitarlo debemos dejar al final el setNextUrl, ya que esto hace que asi se renderice inicialmente, persista su estado null y por tanto no se llame al metodo de recarga.
La mejor respuesta
En la numeración de los pokemons no usen order, usen id
¿Se dan cuenta de que hay números que se brincan en la lista?
Voy a revisarlo.
Es porque se esta tomando el order y no el id.
Renderizando en orden
Si alguien es como yo y le "estresa" un poco que los pokemons no se rendericen en orden, puden arreglarlo simplemente agregando:
const sorted = pokemons.sort((a, b)=> a.id- b.id)
Y usar esa variable en el data del FlatList.
por si no les aparece el spinner le agregan la propiedad color al componente
<ActivityIndicator size="large" color="#000" style={styles.spinner} />
¿Por qué los primeros 20 pokemons no me los muestra?
Mira en los aportes el comentario de: Effren Anthony Peña Sánchez ... esa es la solución
justo me paso eso
Les comparto mi repo, es React Native con Typescript, tiene algunas diferencias con respecto a lo visto en clase, sobre todo el tipado:
Durante mi trabajo en el ejercicio, me topé con un problema que afectaba tanto la carga inicial como el desplazamiento de la página. En relación a la carga, noté que los primeros 20 Pokémon solo se mostraban por un breve momento, mientras que la página comenzaba directamente desde el Pokémon número 20. Además, el problema también afectaba al scroll, ya que el spinner de carga apenas se mostraba antes de desaparecer rápidamente. Para resolver este inconveniente, realicé algunas modificaciones en los archivos Pokedex.js y PokemonList.js.
En Pokedex.js, introduje el uso del estado "loading" para controlar el proceso de carga de datos. Para evitar solicitudes duplicadas, agregué una validación y utilicé el bloque "finally" después del bloque "try-catch" en la función "loadPokemons". De esta manera, aseguré que el estado "loading" se estableciera en falso incluso en caso de error, garantizando así su restablecimiento correcto después de completar la carga, independientemente del resultado.
Respecto a PokemonList.js, opté por utilizar la variable "loading" en lugar de "isNext" para gestionar el estado de carga. Antes de llamar a la función "loadPokemons()", verificaba si "loading" era falso. Esta medida garantizaba que no se iniciara una nueva carga si ya había una en curso. Esto resultaba especialmente relevante debido a la estructura de la clase, donde "loadMore" se llamaba directamente al llegar al final de la lista.
A continuación mi codigo corregido:
Pokedex.js
PokemonList.js
Recomiendo que no usen useEffect para hacer fetching de datos. Hay librerías maravillosas que se encargan de esto.
Yo personalmente uso SWR (de vercel) en mi trabajo, pero decidí aprender a usar @tanstack/react-query para este curso.
Acá dejo mi repo por si alguien quiere ver cómo lo hice.
Me surgen dudas con el funcionamiento de la carga, ya que tengo un error en un dispositivo fisico que dice que la lista es demasiado larga y deja de enviar las peticiones despues de cierto numero.
Analizando el error de que se brinca los primeros 20 después de unos segundos, opte por ponerlo después de la captura de los pokemons para evitar que se actualizara antes de mostrarlos les dejo el código y cualquier retroalimentación es bienvenida, const loadPokemons = async ()** =>{ try{** const response = await getPokemonsApi(nextUrl)//Se trae la informacion de la api const pokemonsArray =** []; for await (const pokemon of response.results){ //console.log(pokemon) const pokemonDetails = await getPokemonDetailsByUrlApi(pokemon.url) pokemonsArray.push({ id: pokemonDetails.id, name: pokemonDetails.name, type: pokemonDetails.types[0].type.name, order: pokemonDetails.order, imagen:pokemonDetails.sprites.other['official-artwork'].front_default })** } setpokemons([...pokemons,...pokemonsArray])//te genera una sola lista de los elementos de ambas listas setNextUrl(response.next)** }catch (error){ console.error(error)** }** }
woo React native no lo deja casi hecho no es nada complejo comparado a web