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.
Inicializando la app
Creación del proyecto: PokeDex
Configuración de React Navigation
Diseñando la Tab navigation con iconos
Stack de navegación para Pokedex
API y listado de Pokemon
Consumo de APIs con fetch: PokeApi
Información de todos los Pokemon
Lista de Pokemon con FlatList
Renderizando la PokemonCard
Clasificación por colores y utilidades de Lodash
Infinite scrolling con FlatList
Añadiendo StyleSheet a la lista
Detalles del Pokemon
Vista y navegación al detalle del Pokemon
Consultando la API de detalles del Pokemon
Maquetación del Pokemon: header
Maquetación del Pokemon: sección de tipos
Maquetación del Pokemon: estadísticas
Maquetación del Pokemon: barra de navegación
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Agustín Navarro Galdon
Aportes 16
Preguntas 2
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.
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 {ActivityIndicator, FlatList, Platform, StyleSheet} from 'react-native';
import React from 'react';
import PokemonCard from './PokemonCard';
const PokemonList = ({pokemons, loadPokemons, isNext, isLoading}) => {
const loadMore = () => {
loadPokemons();
};
return (
<FlatList
data={pokemons}
numColumns={2}
showsVerticalScrollIndicator={false}
keyExtractor={pokemon => String(pokemon.id)}
renderItem={({item}) => <PokemonCard {...item} />}
contentContainerStyle={styles.flatListContentContainer}
onEndReached={!isLoading && isNext && loadMore}
onEndReachedThreshold={0.1}
ListFooterComponent={
isLoading &&
isNext && (
<ActivityIndicator
size="large"
style={styles.spinner}
color="#AEAEAE"
/>
)
}
/>
);
};
export default PokemonList;
const styles = StyleSheet.create({
flatListContentContainer: {
paddingHorizontal: 5,
marginTop: Platform.OS === 'android' ? 30 : 0,
},
spinner: {
marginTop: 20,
marginBottom: Platform.OS === 'android' ? 90 : 60,
},
});
import {SafeAreaView} from 'react-native';
import React, {useCallback, useEffect, useState} from 'react';
import {getPokemonDetailsByUrlApi, getPokemonsApi} from '../api/pokemon';
import PokemonList from '../components/PokemonList';
const Pokedex = () => {
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 loading
const loadPokemons = useCallback(async () => {
try {
// inicializamos la "carga" del request
setLoading(true);
const {results: pokemonsResponse, next: nextPokemonListUrl} =
await getPokemonsApi(nextUrl);
setNextUrl(nextPokemonListUrl);
const pokemonsArray = [];
for await (const pokemon of pokemonsResponse) {
const pokemonDetails = await getPokemonDetailsByUrlApi(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 false
setLoading(false);
}
}, [pokemons, nextUrl]);
return (
<SafeAreaView>
<PokemonList
pokemons={pokemons}
loadPokemons={loadPokemons}
isNext={nextUrl}
isLoading={loading}
/>
</SafeAreaView>
);
};
export default Pokedex;
Espero que les sea útil 🤗
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.
En la numeración de los pokemons no usen order, usen id
return (
<TouchableWithoutFeedback onPress={goToPokemon}>
<View style={styles.card}>
<View style={styles.spacing}>
<View style={bgStyles}>
<Text style={styles.number}>
#{`${pokemon.id}`.padStart(3, 0)}
</Text>
<Text style={styles.name}>{capitalize(pokemon.name)}</Text>
<Image source={{ uri: pokemon.image }} style={styles.image} />
</View>
</View>
</View>
</TouchableWithoutFeedback>
);
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} />
woo React native no lo deja casi hecho no es nada complejo comparado a web
Excelente Clase!
Wow que excelente clase 💚
a mi el loader no me funciona, no aparece.
commit - https://github.com/NightDreams/pokedex/commit/822bd9195300b1632042777dc894a1ada06fdc69
android . React navigation 6 expo 44
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?