No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Infinite scrolling con FlatList

10/17
Recursos

Aportes 14

Preguntas 2

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

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

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 鈥渋sNext鈥 en el componente 鈥淧okemonList鈥, 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茅 鈥渓oading鈥, de manera que solo se haga el request cuando se haya completado el anterior. Mis componentes quedaron de esta manera

PokemonList.js

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,
  },
});

Pokedex.js (screens)

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>
  );
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) } } ```

por si no les aparece el spinner le agregan la propiedad color al componente
<ActivityIndicator size=鈥渓arge鈥 color="#000" style={styles.spinner} />

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.
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.
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)**聽 聽 聽 聽 }**聽 聽 }

Renderizando en orden

Si alguien es como yo y le 鈥渆stresa鈥 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.

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