Lecciones aprendidas construyendo una app del clima con React
react typescript api clima desarrollo-web frontend javascript

Lecciones aprendidas construyendo una app del clima con React

Descubre los desafíos técnicos, soluciones implementadas y lecciones aprendidas al desarrollar una aplicación del clima completa con React, TypeScript y APIs externas.

⏱️ 6 min de lectura
Lecciones aprendidas construyendo una app del clima con React

Lecciones aprendidas construyendo una app del clima con React

Desarrollar una aplicación del clima puede parecer simple a primera vista, pero la realidad es que presenta desafíos técnicos interesantes. Te comparto mi experiencia y las lecciones aprendidas al construir una app completa con React y TypeScript.

El proyecto

La aplicación del clima que desarrollé incluye:

  • Búsqueda de ciudades por nombre

  • Pronóstico del tiempo actual y extendido

  • Información detallada (humedad, viento, presión)

  • Interfaz responsive y accesible

  • Integración con múltiples APIs

  • Enlaces del proyecto:

  • Website
  • API utilizada: OpenWeatherMap API

Puedes probar la aplicación directamente haciendo click en el enlace de arriba. La API de OpenWeatherMap es gratuita para uso básico y muy confiable para proyectos de desarrollo.

Desafíos técnicos encontrados

1. Integración de APIs

Uno de los mayores desafíos fue integrar múltiples APIs de manera eficiente:

interface WeatherAPI {
  getCurrentWeather(city: string): Promise<WeatherData>;
  getForecast(city: string): Promise<ForecastData>;
  getGeocoding(city: string): Promise<GeocodingData>;
}

class OpenWeatherMapAPI implements WeatherAPI {
  private apiKey: string;
  private baseUrl = 'https://api.openweathermap.org/data/2.5';

  constructor(apiKey: string) {
    this.apiKey = apiKey;
  }

  async getCurrentWeather(city: string): Promise<WeatherData> {
    const response = await fetch(
      `${this.baseUrl}/weather?q=${city}&appid=${this.apiKey}&units=metric`
    );
    
    if (!response.ok) {
      throw new Error(`Error: ${response.status}`);
    }
    
    return response.json();
  }
}

2. Manejo de estados complejos

El estado de la aplicación se volvió complejo rápidamente:

interface WeatherState {
  currentWeather: WeatherData | null;
  forecast: ForecastData | null;
  loading: boolean;
  error: string | null;
  searchHistory: string[];
  favorites: string[];
}

const initialState: WeatherState = {
  currentWeather: null,
  forecast: null,
  loading: false,
  error: null,
  searchHistory: [],
  favorites: []
};

3. Optimización del rendimiento

Implementé varias técnicas para mejorar el rendimiento:

// Debounce para la búsqueda
const useDebounce = (value: string, delay: number) => {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
};

// Memoización de componentes
const WeatherCard = React.memo(({ weather }: { weather: WeatherData }) => {
  return (
    <div className="weather-card">
      <h3>{weather.city}</h3>
      <p>{weather.temperature}°C</p>
    </div>
  );
});

Soluciones implementadas

1. Patrón de repositorio para APIs

Implementé un patrón de repositorio para manejar múltiples APIs:

class WeatherRepository {
  private apis: WeatherAPI[];

  constructor(apis: WeatherAPI[]) {
    this.apis = apis;
  }

  async getWeatherData(city: string): Promise<WeatherData> {
    for (const api of this.apis) {
      try {
        return await api.getCurrentWeather(city);
      } catch (error) {
        console.warn(`API ${api.constructor.name} failed:`, error);
        continue;
      }
    }
    throw new Error('All APIs failed');
  }
}

2. Sistema de caché inteligente

Desarrollé un sistema de caché para evitar llamadas innecesarias:

class WeatherCache {
  private cache = new Map<string, { data: any; timestamp: number }>();
  private ttl = 5 * 60 * 1000; // 5 minutos

  set(key: string, data: any): void {
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });
  }

  get(key: string): any | null {
    const item = this.cache.get(key);
    if (!item) return null;

    if (Date.now() - item.timestamp > this.ttl) {
      this.cache.delete(key);
      return null;
    }

    return item.data;
  }
}

3. Manejo de errores robusto

Implementé un sistema de manejo de errores que mejora la experiencia del usuario:

const useWeatherData = (city: string) => {
  const [state, setState] = useState<WeatherState>(initialState);

  const fetchWeather = async () => {
    if (!city.trim()) return;

    setState(prev => ({ ...prev, loading: true, error: null }));

    try {
      const [weather, forecast] = await Promise.all([
        weatherAPI.getCurrentWeather(city),
        weatherAPI.getForecast(city)
      ]);

      setState(prev => ({
        ...prev,
        currentWeather: weather,
        forecast,
        loading: false,
        searchHistory: [...new Set([city, ...prev.searchHistory])].slice(0, 10)
      }));
    } catch (error) {
      setState(prev => ({
        ...prev,
        error: error instanceof Error ? error.message : 'Error desconocido',
        loading: false
      }));
    }
  };

  return { ...state, fetchWeather };
};

Lecciones aprendidas

1. Planificación de la arquitectura es crucial

  • Definir interfaces claras desde el inicio
  • Separar responsabilidades entre componentes
  • Pensar en la escalabilidad del código

2. El manejo de errores no es opcional

  • Implementar fallbacks para cada posible fallo
  • Proporcionar mensajes de error útiles al usuario
  • Logging detallado para debugging

3. La optimización debe ser continua

  • Medir el rendimiento constantemente
  • Implementar lazy loading y code splitting
  • Usar herramientas como React DevTools Profiler

4. La experiencia del usuario importa

  • Estados de carga claros
  • Feedback inmediato para las acciones
  • Diseño responsive y accesible

Métricas de rendimiento

Los resultados finales fueron satisfactorios:

  • Lighthouse Performance: 95/100
  • First Contentful Paint: 1.2s
  • Largest Contentful Paint: 2.1s
  • Cumulative Layout Shift: 0.05
  • First Input Delay: 45ms

Próximos pasos

Para futuras versiones, planeo implementar:

  • PWA capabilities para uso offline
  • Notificaciones push para alertas del clima
  • Machine Learning para predicciones más precisas
  • Integración con wearables para datos en tiempo real

Conclusión

Desarrollar esta aplicación del clima fue una experiencia invaluable que me enseñó mucho sobre:

  • Arquitectura de aplicaciones React complejas
  • Integración de APIs externas
  • Optimización de rendimiento
  • Manejo de estados y errores
  • Experiencia del usuario

La clave del éxito fue la planificación cuidadosa y la implementación iterativa. Cada desafío superado me hizo un mejor desarrollador.

¿Has desarrollado alguna aplicación similar? ¿Qué desafíos encontraste? Me encantaría leer tu experiencia en los comentarios.

Recursos adicionales

Si te interesa desarrollar una aplicación similar, aquí tienes algunos recursos útiles:


¿Te gustó este post? Sígueme en GitHub para más contenido técnico sobre desarrollo web.

¡Hablemos por WhatsApp!