Práctica: App de clima con estado dinámico

Lectura
30 min~6 min lectura

Concepto clave

En el desarrollo de aplicaciones móviles con React Native y Expo, la gestión de estado dinámico es fundamental para crear interfaces de usuario que respondan a datos cambiantes en tiempo real. Imagina una aplicación de clima: la temperatura, condiciones atmosféricas y pronósticos no son estáticos; se actualizan constantemente. El estado dinámico permite que tu app refleje estos cambios sin necesidad de recargar manualmente, similar a cómo un termómetro digital ajusta su lectura cuando la temperatura ambiente varía.

En React Native, el estado se maneja típicamente con useState y useEffect de React, o con librerías como Context API o Redux para casos más complejos. Para una app de clima, el estado dinámico incluye no solo los datos meteorológicos actuales, sino también el estado de carga, errores de red y preferencias del usuario. Esto crea una experiencia fluida donde la interfaz se adapta automáticamente a nuevas informaciones, manteniendo al usuario informado sin interrupciones.

Cómo funciona en la práctica

Vamos a construir una app de clima paso a paso. Primero, configura un proyecto en Expo usando npx create-expo-app WeatherApp. Luego, instala dependencias esenciales: expo-location para obtener la ubicación del usuario y axios para hacer peticiones a una API de clima como OpenWeatherMap. En la práctica, el flujo es: 1) Obtener permisos de ubicación, 2) Hacer una solicitud HTTP a la API con las coordenadas, 3) Actualizar el estado con la respuesta, y 4) Renderizar los datos en la UI.

Un ejemplo básico: al iniciar la app, se muestra un indicador de carga mientras se obtiene la ubicación. Una vez que la API responde, el estado cambia de loading a loaded, y la temperatura, icono y descripción se muestran en pantalla. Si hay un error (por ejemplo, sin conexión a internet), el estado cambia a error y se muestra un mensaje amigable. Este ciclo de estado—carga, éxito, error—es común en apps con datos dinámicos y asegura una experiencia robusta.

Código en acción

Aquí tienes un ejemplo funcional de un componente de clima usando useState y useEffect. Este código es copiable y ejecutable en un proyecto Expo:

import React, { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native';
import * as Location from 'expo-location';
import axios from 'axios';

const WeatherApp = () => {
  const [weather, setWeather] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchWeather = async () => {
      try {
        let { status } = await Location.requestForegroundPermissionsAsync();
        if (status !== 'granted') {
          setError('Permiso de ubicación denegado');
          setLoading(false);
          return;
        }
        let location = await Location.getCurrentPositionAsync({});
        const { latitude, longitude } = location.coords;
        const response = await axios.get(
          `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=TU_API_KEY&units=metric`
        );
        setWeather(response.data);
        setLoading(false);
      } catch (err) {
        setError('Error al obtener el clima');
        setLoading(false);
      }
    };
    fetchWeather();
  }, []);

  if (loading) {
    return (
      
        
        Cargando clima...
      
    );
  }

  if (error) {
    return (
      
        {error}
      
    );
  }

  return (
    
      {weather.main.temp}°C
      {weather.weather[0].description}
    
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  temp: { fontSize: 48, fontWeight: 'bold' },
  error: { color: 'red' }
});

export default WeatherApp;

Para mejorar este código, podríamos refactorizarlo usando Context API para manejar el estado globalmente, especialmente si añadimos más características como pronósticos futuros. Aquí el antes y después:

Antes: Estado local en el componente, difícil de compartir con otros componentes.

Después: Crear un contexto WeatherContext que proporcione el estado a toda la app, facilitando el acceso desde múltiples pantallas.

// WeatherContext.js
import React, { createContext, useState, useContext } from 'react';

const WeatherContext = createContext();

export const WeatherProvider = ({ children }) => {
  const [weather, setWeather] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  return (
    
      {children}
    
  );
};

export const useWeather = () => useContext(WeatherContext);

Errores comunes

  • No manejar estados de carga y error: Muchos desarrolladores olvidan incluir indicadores de carga o mensajes de error, lo que resulta en una UI bloqueada o confusa. Siempre define estados para loading, data y error.
  • Actualizar estado en loops o condiciones incorrectas: Evita llamar setState dentro de bucles sin control, ya que puede causar renders infinitos. Usa useEffect con dependencias claras.
  • No limpiar suscripciones o efectos: En componentes con timers o listeners, olvidar la función de cleanup en useEffect puede llevar a fugas de memoria. Siempre retorna una función de limpieza si es necesario.
  • Usar estado local para datos globales: Para datos compartidos entre múltiples componentes, el estado local puede volverse engorroso. Considera Context API o Redux en su lugar.
  • Ignorar la optimización de rendimiento: Actualizar estado con objetos grandes o frecuentemente puede ralentizar la app. Usa useMemo o useCallback para optimizar cuando sea necesario.

Checklist de dominio

  1. Puedo configurar un proyecto Expo e instalar dependencias para una app de clima.
  2. Sé usar useState y useEffect para manejar estado dinámico con datos de API.
  3. Puedo implementar estados de carga, éxito y error en la UI.
  4. Entiendo cuándo refactorizar estado local a Context API para compartir datos.
  5. Sé evitar errores comunes como fugas de memoria o renders infinitos.
  6. Puedo integrar permisos de ubicación y hacer peticiones HTTP seguras.
  7. Soy capaz de probar la app en dispositivos iOS y Android usando Expo Go.

Construye una app de clima con pronóstico extendido

Sigue estos pasos para crear una app de clima que muestre el clima actual y un pronóstico de 5 días, aplicando gestión de estado dinámico en React Native con Expo:

  1. Configura el proyecto: Crea una nueva app Expo llamada WeatherForecastApp y instala expo-location, axios y @react-navigation/native para navegación.
  2. Implementa el estado: En un componente principal, usa useState para manejar: clima actual, pronóstico de 5 días, estado de carga y errores. Inicializa los estados apropiadamente.
  3. Obtén datos de la API: En useEffect, solicita permisos de ubicación y haz dos peticiones a OpenWeatherMap: una para el clima actual y otra para el pronóstico (endpoint /forecast). Actualiza el estado con las respuestas.
  4. Diseña la UI: Crea dos pantallas: una para el clima actual (temperatura, icono, descripción) y otra para el pronóstico (lista de días con temperaturas). Usa React Navigation para cambiar entre ellas.
  5. Maneja errores y carga: Muestra un ActivityIndicator durante la carga y un mensaje de error si falla la API o los permisos. Asegúrate de que la UI responda a cambios de estado.
  6. Refactoriza con Context API: Si el estado se vuelve complejo, mueve la lógica a un WeatherContext para compartir datos entre pantallas sin prop drilling.
  7. Prueba la app: Ejecuta en un emulador o dispositivo físico usando Expo Go, verifica que los datos se actualicen dinámicamente y la navegación funcione.
Pistas
  • Usa la API de OpenWeatherMap: necesitarás una clave gratuita de su sitio web para hacer peticiones.
  • Para el pronóstico de 5 días, el endpoint /forecast devuelve datos en intervalos de 3 horas; agrupa por día para simplificar.
  • Considera usar FlatList para mostrar el pronóstico, ya que maneja listas largas eficientemente en React Native.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.