Guía 14

Guía 14

DAWM / Proyecto03

Objetivo general

Desarrollar un dashboard interactivo y visualmente intuitivo utilizando tecnologías web modernas, como React, que permita a los usuarios monitorear en tiempo real métricas clave del clima.

Actividades en clases

  1. Clona localmente tu repositorio dashboard.

Componente App: hook - useEffect

  1. En el componente src/App.tsx, agregue:

    • La referencia al hook useEffect.
     {/* Hooks */ }
     import { useEffect } from 'react';
    
    • La interfaz Indicator:
     {/* Hooks */ }
     ...
    
     interface Indicator {
       title?: String;
       subtitle?: String;
       value?: String;
     }
    
     function App() { ... }
    
    • El hook useEffect para reaccionar únicamente después del renderizado ( fase de Montaje en el ciclo de vida ) en el DOM.
     ...
    
     function App() {
    
         {/* Hook: useEffect */}
         useEffect( ()=>{}, [] )
    
         return ( ... )
     }
    
  2. En el hook useEffect del componente src/App.tsx

    • Agregue y ejecute la función asíncrona request dentro de la función para el efecto secundario.
     function App() {
    
         {/* Hook: useEffect */}
         useEffect(()=>{
    
             let request = async () => { }
    
             request();
    
         },[])
    
         return ( ... )
     }
    
    • Agregue una petición asíncrona con fetch dentro de la función de autoejecución async.
     function App() {
    
         {/* Hook: useEffect */}
         useEffect(()=>{
    
             let request = async () => {
    
                 {/* Request */}
                 let API_KEY = "OPENWEATHERMAP' API KEY"
                 let response = await fetch(`https://api.openweathermap.org/data/2.5/forecast?q=Guayaquil&mode=xml&appid=${API_KEY}`)
                 let savedTextXML = await response.text();
    
             }
    
             request();
    
         },[])
    
         return ( ... )
     }
    
    • Agregue el analizador (parser) de XML
     function App() {
    
         {/* Hook: useEffect */}
         useEffect(()=>{
    
             let request = async () => {
    
                 {/* Request */}
    
                 ...
    
                 {/* XML Parser */}
                 const parser = new DOMParser();
                 const xml = parser.parseFromString(savedTextXML, "application/xml");
    
             }
    
             request();
    
         },[])
    
         return ( ... )
     }	
    
    • Agregue el arreglo para almacenar temporalmente los resultados, extraiga el contenido del xml mediante el API del DOM (métodos getElementsByTagName y getAttribute) y guarde los resultado en arreglo. Revise la estructura del documento XML para extraer los datos necesarios.
     function App() {
    
         {/* Hook: useEffect */}
    
         useEffect(()=>{
    
             let request = async () => {
    
                 ...
    
                 {/* XML Parser */}
    
                 ...
    
                 {/* Arreglo para agregar los resultados */}
    
                 let dataToIndicators : Indicator[] = new Array<Indicator>();
    
                 {/* 
                     Análisis, extracción y almacenamiento del contenido del XML 
                     en el arreglo de resultados
                 */}
    
                 let name = xml.getElementsByTagName("name")[0].innerHTML || ""
                 dataToIndicators.push({"title":"Location", "subtitle": "City", "value": name})
    
                 let location = xml.getElementsByTagName("location")[1]
    
                 let latitude = location.getAttribute("latitude") || ""
                 dataToIndicators.push({ "title": "Location", "subtitle": "Latitude", "value": latitude })
    
                 let longitude = location.getAttribute("longitude") || ""
                 dataToIndicators.push({ "title": "Location", "subtitle": "Longitude", "value": longitude })
    
                 let altitude = location.getAttribute("altitude") || ""
                 dataToIndicators.push({ "title": "Location", "subtitle": "Altitude", "value": altitude })
    
                 console.log( dataToIndicators )
    
             }
    
             request();
    
    
         },[])
    
         return ( ... )
     }
    
  3. (STOP 1) Compruebe el resultado en el navegador.

Componente App: hook - useState

  1. En el componente src/App.tsx, agregue:

    • La referencia al hook useState.
     import { useEffect, useState } from 'react';
    
    • La variable de estado indicators y la función de actualización setIndicators. El valor predeterminado de la variable de estado es un arreglo vacío del tipo Indicator.
     function App() {
    
         {/* Variable de estado y función de actualización */}
         let [indicators, setIndicators] = useState<Indicator[]>([])
    
         {/* Hook: useEffect */}
         ...
    
         return ( ... )
     }
    
  2. En el hook useEffect del componente src/App.tsx, modifique la variable de estado mediante la función de actualización.

     function App() {
    
         {/* Hook: useEffect */}
         useEffect(()=>{
    
             let request = async () => {
    
                 ...
     
                 // console.log( dataToIndicators )
    
                 {/* Modificación de la variable de estado mediante la función de actualización */}
                 setIndicators( dataToIndicators )
    
             }
    
             request()
    
         },[])
    
         return ( ... )
    
     }
    
  3. Agregue la función renderIndicators encargada de iterar la variable de estado indicators usando la plantilla de elementos <Grid> e <IndicatorWeather>

     function App() {
    
         {/* Hook: useEffect */}
         useEffect(()=>{ ... },[])
    
         let renderIndicators = () => {
    
             return indicators
                     .map(
                         (indicator, idx) => (
                             <Grid key={idx} size={{ xs: 12, xl: 3 }}>
                                 <IndicatorWeather 
                                     title={indicator["title"]} 
                                     subtitle={indicator["subtitle"]} 
                                     value={indicator["value"]} />
                             </Grid>
                         )
                     )
    					
         }
    
         {/* JSX */}
         return ( ... )
     }
    
  4. En el componente src/App.tsx:

    • Comente el grid de indicadores previo.
    • Llame a la función renderIndicators.
     ...
    
     function App() {
    
         ...
    
         {/* JSX */}
         return (
    
             <Grid container spacing={5}>
    				
                 {/* Indicadores */}
                 {/* <Grid size=> ... </Grid> */}
    
                 {renderIndicators()}
    				
             </Grid>
    
             ...
         )
     }
    
  5. (STOP 2) Compruebe el resultado en el navegador.

LocalStorage

  1. En el componente src/App.tsx, agregue:

    • La variable de estado owm y la función de actualización setOWM. El valor predeterminado de la variable de estado es el valor en localStorage openWeatherMap.
     function App() {
    
         {/* Variable de estado y función de actualización */}
         let [indicators, setIndicators] = useState<Indicator[]>([])
         let [owm, setOWM] = useState(localStorage.getItem("openWeatherMap"))
    
         {/* Hook: useEffect */}
         ...
    
         return ( ... )
     }
    
  2. En el hook useEffect del componente src/App.tsx, agregue:

    • La referencia a las claves del LocalStorage: openWeatherMap y expiringTime
     ...
     function App() {
    
         {/* Hook: useEffect */}
         useEffect(() => {
    
             let request = async () => {
    
                 {/* Referencia a las claves del LocalStorage: openWeatherMap y expiringTime */}
                 let savedTextXML = localStorage.getItem("openWeatherMap") || "";
                 let expiringTime = localStorage.getItem("expiringTime");
    
                 {/* Request */}
                 ...
    
                 {/* XML Parser */}
                 ...
             }
    
         	request();
    
     	}, [])
     }
    
    • Obtenga la estampa de tiempo actual
     ...
     function App() {
    
         {/* Hook: useEffect */}
         useEffect(() => {
    
             let request = async () => {
    
                 {/* Referencia a las claves del LocalStorage: openWeatherMap y expiringTime */}
                 ...
    
                 {/* Obtenga la estampa de tiempo actual */}
                 let nowTime = (new Date()).getTime();
    
                 {/* Request */}
                 ...
    
                 {/* XML Parser */}
                 ...
             }
    
         	request();
    
     	}, [])
     }
    
    • Verifique si no existe la clave expiringTime o si la estampa de tiempo actual supera el tiempo de expiración para realizar la petición asincrónica
     ...
     function App() {
    
         {/* Hook: useEffect */}
         useEffect(() => {
    
             let request = async () => {
    
                 {/* Referencia a las claves del LocalStorage: openWeatherMap y expiringTime */}
                 ...
    
                 {/* Obtenga la estampa de tiempo actual */}
                 ...
    
                 {/* Verifique si es que no existe la clave expiringTime o si la estampa de tiempo actual supera el tiempo de expiración */}
                 if(expiringTime === null || nowTime > parseInt(expiringTime)) {
    
                     {/* Request */}
                     ...
    				
                 }
    
                 {/* XML Parser */}
                 ...
             }
    
         	request();
    
     	}, [])
     }
    
    • Luego de realizar la petición asincrónica, calcule y almacene el tiempo de expiración, almacene el texto en la clave openWeatherMap y use la función de actualización con el resultado de la petición.
     ...
     function App() {
    
         {/* Hook: useEffect */}
         useEffect(() => {
    
             let request = async () => {
    
                 {/* Referencia a las claves del LocalStorage: openWeatherMap y expiringTime */}
                 ...
    
                 {/* Obtenga la estampa de tiempo actual */}
                 ...
    
                 {/* Verifique si es que no existe la clave expiringTime o si la estampa de tiempo actual supera el tiempo de expiración */}
                 if(expiringTime === null || nowTime > parseInt(expiringTime)) {
    
                     {/* Request */}
                     ...
    
                     {/* Tiempo de expiración */}
                     let hours = 0.01
                     let delay = hours * 3600000
                     let expiringTime = nowTime + delay
    
    
                     {/* En el LocalStorage, almacene el texto en la clave openWeatherMap, estampa actual y estampa de tiempo de expiración */}
                     localStorage.setItem("openWeatherMap", savedTextXML)
                     localStorage.setItem("expiringTime", expiringTime.toString())
                     localStorage.setItem("nowTime", nowTime.toString())
    
                     {/* DateTime */}
                     localStorage.setItem("expiringDateTime", new Date(expiringTime).toString())
                     localStorage.setItem("nowDateTime", new Date(nowTime).toString())
    
                     {/* Modificación de la variable de estado mediante la función de actualización */ }
                     setOWM( savedTextXML )
                 }
    
                 {/* XML Parser */}
                 ...
             }
    
         	request();
    
     	}, [])
     }
    
    • Valide el procesamiento con el valor de savedTextXML.
     ...
     function App() {
    
         {/* Hook: useEffect */}
         useEffect(() => {
    
             let request = async () => {
    
                 {/* Referencia a las claves del LocalStorage: openWeatherMap y expiringTime */}
                 ...
    
                 {/* Obtenga la estampa de tiempo actual */}
                 ...
    
                 {/* Verifique si es que no existe la clave expiringTime o si la estampa de tiempo actual supera el tiempo de expiración */}
                 if(expiringTime === null || nowTime > parseInt(expiringTime)) {
    
                     ...
                 }
    
                 {/* Valide el procesamiento con el valor de savedTextXML */}
                 if( savedTextXML ) {
    
                         {/* XML Parser */}
    
                     	{/* Arreglo para agregar los resultados */ }
    
                     	{/* 
                            Análisis, extracción y almacenamiento del contenido del XML 
                            en el arreglo de resultados
                         */}
    
                     	{/* Modificación de la variable de estado mediante la función de actualización */ }
    
                 }
             }
    
         	request();
    
     	}, [])
     }
    
    • Habilite la ejecución cada vez que cambie la variable de estado owm.
     ...
     function App() {
    
         {/* Hook: useEffect */}
         useEffect(() => {
    
             let request = async () => {
    
                 ...
             }
    
         	request();
    
     	}, [owm])
     }
    
  3. (STOP 3) Compruebe el resultado en el navegador.
  4. Versiona local y remotamente el repositorio dashboard.
  5. Despliega la aplicación dashboard.

Documentación

Fundamental

Términos

React Lifecycle, React Lifecycle phases, parser

Referencias