Implementación del tab Explore con FlatList y carga asíncrona

Clase 19 de 23Curso de Fundamentos de React Native

Resumen

Optimiza la experiencia del tab explore en una app de hábitos: carrusel horizontal con FlatList, estados por categoría, carga asíncrona con useEffect, manejo de errores con try/catch y alertas nativas. Aquí verás cómo pasar de ScrollView a un carrusel eficiente, reutilizar secciones y conectar el servicio de Explore con el de hábitos sin duplicar código.

¿Cómo implementar el tab explore con flatlist horizontal?

Al migrar a FlatList obtienes rendimiento y un carrusel horizontal limpio. La lista recibe la data de sugerencias, un renderItem que entrega la tarjeta y un keyExtractor basado en el id. Además, se prepara un estado de carga con estilos de sombras para mostrar un placeholder mientras llegan los datos del servicio.

// renderItem: retorna la tarjeta reutilizable
const renderItem = ({ item }) => (
  <ExploreCard
    emoji={item.emoji}
    title={item.title}
    subtitle={item.subtitle}
    onPress={() => Alert.alert(item.title, 'Acción ejecutada.')}
  />
);

// keyExtractor: id único por sugerencia
const keyExtractor = (item) => String(item.id);

// carrusel horizontal con FlatList
<FlatList
  horizontal
  data={data}
  keyExtractor={keyExtractor}
  renderItem={renderItem}
  showsHorizontalScrollIndicator={false}
/>

¿Cómo crear el renderitem y el keyextractor?

  • Usa un identificador estable para el keyExtractor.
  • Retorna la tarjeta con emoji, título, subtítulo y acción.
  • Evita lógica extra dentro de renderItem: delega en la tarjeta.
  • Muestra alerta nativa con Alert.alert al seleccionar.

¿Cómo construir la sección reutilizable por categoría?

Agrupa título y carrusel en una sección que reciba props. Así reduces duplicación y renders innecesarios.

// sección reutilizable por categoría
const Section = ({ title, data /* Suggestion[] | null */ }) => (
  <View style={{ marginVertical: 12 }}>
    <Text style={styles.sectionTitle}>{title}</Text>
    {data ? (
      <FlatList horizontal data={data} renderItem={renderItem} keyExtractor={keyExtractor} />
    ) : (
      // placeholder de carga con sombras
      <ExploreCard loading />
    )}
  </View>
);

¿Qué estados y tipos definen las sugerencias dinámicas?

Se manejan dos categorías como estados independientes: energía y focus. Ambos usan el tipo suggestion definido en el servicio. Así, cada carrusel recibe su propio arreglo o null cuando aún no hay data. La tarjeta deja de “quemar” la prioridad, ya que ahora se lee desde la sugerencia.

// estados por categoría y flag de carga
const [energy, setEnergy] = useState(/* Suggestion[] | null */ null);
const [focus, setFocus] = useState(/* Suggestion[] | null */ null);
const [isLoading, setIsLoading] = useState(false);

// mapeo a la tarjeta: usa s.title y prioridad de la data
<ExploreCard
  title={s.title}
  priority={s.priority}
  /* ...otros props */
/>

¿Cómo mapear suggestion a la tarjeta y prioridad?

  • Usa s.title como título que llega del servicio.
  • Toma priority desde la sugerencia, no hardcodees valores.
  • Mantén el tipo suggestion consistente en todo el flujo.
  • Gestiona energía y focus como fuentes separadas de data.

¿Cómo cargar datos con useeffect y manejar errores?

Al entrar al tab, un useEffect dispara una función async que llama al servicio de sugerencias para ambas categorías. Se controla el estado de carga, se actualizan los estados locales y se captura cualquier error con console.warn. Al finalizar, se restablece el flag para indicar que terminó la operación.

useEffect(() => {
  let mounted = true;
  const loadSuggestions = async () => {
    setIsLoading(true);
    try {
      const a = await suggestFirst('energia');
      const b = await suggestFirst('focus');
      if (!mounted) return;
      setEnergy(a);
      setFocus(b);
    } catch (e) {
      console.warn('No se pudieron cargar las sugerencias', e);
    } finally {
      setIsLoading(false);
    }
  };

  loadSuggestions();
  return () => { mounted = false; };
}, []);

¿Cómo manejar errores y alertas sin fricción?

  • Implementa try/catch para capturar fallos de red o servicio.
  • Usa console.warn para diagnóstico no intrusivo.
  • Muestra Alert.alert al añadir desde el carrusel: comunica éxito al usuario.
  • Asegura un placeholder visible mientras se resuelve la promesa.

¿Te gustaría ver más patrones para listas horizontales y estados compartidos entre tabs? Cuéntame qué categoría agregarías o qué mejora probarías a continuación.