Guía 14

Guía 14

DAWM / Proyecto03

Actividades previas

Revise el diagrama interactivo de los hooks en el ciclo de vida de los componentes.

Fuente: React Hooks Lifecycle Diagram

Actividades en clases

  1. Clona localmente tu repositorio dashboard.

Hook: useEffect - Petición asíncrona de un XML

  1. En el componente App.tsx, agregue la referencia al hook useEffect.

     import { useEffect } from 'react';
     ...
    
  2. En el componente App.tsx, agregue el hook useEffect para reaccionar después del primer renderizado ( fase de Montaje en el ciclo de vida ) en el DOM.

     function App() {
    
         {/* Variable de estado y función de actualización */}
    
         ...
    
         {/* Hook: useEffect */}
    
         {/* Función para el efecto secundario a ejecutar y Arreglo de dependencias */} 
    
    
         useEffect( ()=>{}, [] )
    
         ...
     }
    
  3. En el hook useEffect del componente App.tsx

    • (1) Agregue una función de autoejecución async dentro de la función para el efecto secundario.
         ...
    
         {/* Hook: useEffect */}
    
         useEffect(()=>{
    
             (async ()=>{  })()
    
         },[])
    
         ...
    
    • (2) Agregue una petición asíncrona con fetch dentro de la función de autoejecución async.
         ...
    
         {/* Hook: useEffect */}
    
         useEffect(()=>{
    
             (async ()=>{
    
                 {/* Request */}
    
                 let API_KEY = "AQUÍ VA SU API KEY DE OPENWEATHERMAP"
                 let response = await fetch(`https://api.openweathermap.org/data/2.5/forecast?q=Guayaquil&mode=xml&appid=${API_KEY}`)
                 let savedTextXML = await response.text();
    
             })()
    
         },[])
    
         ...
    
    • (3) Agregue el analizador (parser) de XML
         ...
    
         {/* Hook: useEffect */}
    
         useEffect(()=>{
    
             (async ()=>{
    
                 {/* Request */}
    
                 ...
    
                 {/* XML Parser */}
    
                 const parser = new DOMParser();
                 const xml = parser.parseFromString(savedTextXML, "application/xml");
    
             })()
    
         },[])
    
         ...
    
    • (3) 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.
         ...
    
         {/* Hook: useEffect */}
    
         useEffect(()=>{
    
             (async ()=>{
    
                 ...
    
                 {/* XML Parser */}
    
                 ...
    
                 {/* Arreglo para agregar los resultados */}
    
                 let dataToIndicators = new Array()
    
                 {/* 
                     Análisis, extracción y almacenamiento del contenido del XML 
                     en el arreglo de resultados
                 */}
    
                 let location = xml.getElementsByTagName("location")[1]
    
                 let geobaseid = location.getAttribute("geobaseid")
                 dataToIndicators.push(["Location","geobaseid", geobaseid])
    
                 let latitude = location.getAttribute("latitude")
                 dataToIndicators.push(["Location","Latitude", latitude])
    
                 let longitude = location.getAttribute("longitude")
                 dataToIndicators.push(["Location","Longitude", longitude])
    
                 console.log( dataToIndicators )
    
             })()
    
    
         },[])
    
         ...
    
  4. (STOP 1) Compruebe el resultado en el navegador.

Renderización Dinámica con Map

  1. En el componente App.tsx, agregue la referencia al hook useState.

     import { useEffect, useState } from 'react';
     ...
    
  2. En el componente App.tsx, agregue 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.

     function App() {
    
         {/* Variable de estado y función de actualización */}
    
         let [indicators, setIndicators] = useState([])
    
         ...
    
         {/* Hook: useEffect */}
    
         ...
     }
    
  3. En el hook useEffect del componente App.tsx, renderice el arreglo temporal en un arreglo de elementos <Indicator> y modifique la variable de estado mediante la función de actualización.

         ...
    
         {/* Hook: useEffect */}
    
         useEffect(()=>{
    
             (async ()=>{
    
                 ...
    
                 {/* 
                     Análisis, extracción y almacenamiento del contenido del XML 
                     en el arreglo de resultados
                 */}
    
                 ...
    
                 // console.log( dataToIndicators )
    
                 ... 
    
                 {/* Renderice el arreglo de resultados en un arreglo de elementos Indicator */}
    
                 let indicatorsElements = Array.from(dataToIndicators).map(
                     (element) => <Indicator title={element[0]} subtitle={element[1]} value={element[2]} />
                 )
    				
                 {/* Modificación de la variable de estado mediante la función de actualización */}
    
                 setIndicators(indicatorsElements)
    
             })()
    
         },[])
    
         ...
    
  4. En el JSX del componente App.tsx, cambie los elementos Indicator por elementos de la variable de estado.

     ...
    
     function App() {
    
    
         {/* JSX */}
    
         return (
    
             <Grid container spacing={5}>
    			
                 <Grid xs={6} lg={2}>
    
                     {indicators[0]}
    
                     {/* <Indicator title='Precipitación' subtitle='Probabilidad' value={0.13} /> */}
    				
                 </Grid>
    				
                 <Grid xs={6} lg={2}>
    
                     {indicators[1]}
    					
                     {/* <Indicator title='Precipitación' subtitle='Probabilidad' value={0.13} /> */}
    				
                 </Grid>
    				
                 <Grid xs={6} lg={2}>
    
                     {indicators[2]}
    					
                     {/* <Indicator title='Precipitación' subtitle='Probabilidad' value={0.13} /> */}
    				
                 </Grid>
    
             ...
    
             </Grid>
    
         )
     }
    
  5. (STOP 2) Compruebe el resultado en el navegador.

LocalStorage (OPCIONAL)

  1. En el hook useEffect del componente App.tsx, reorganice el código para usar el almacenamiento del navegador (LocalStorage) para guardar la respuesta de la petición asincrónica.

         ...
    
         {/* Hook: useEffect */}
    
         useEffect(()=>{
    
    
             (async ()=>{
    
    				
                 {/* 1. Comente el código anterior con el Request */}
    
                 // {/* Request */ }
    
                 // let API_KEY = "AQUÍ VA SU API KEY DE OPENWEATHERMAP"
                 // let response = await fetch(`https://api.openweathermap.org/data/2.5/forecast?q=Guayaquil&mode=xml&appid=${API_KEY}`)
                 // let savedTextXML = await response.text();
    
    
                 {/* 2. Del LocalStorage, obtiene el valor de las claves openWeatherMap y expiringTime */}
    
                 let savedTextXML = localStorage.getItem("openWeatherMap")
                 let expiringTime = localStorage.getItem("expiringTime")
    
                 {/* 3. Obtenga la estampa de tiempo actual */}
    
                 let nowTime = (new Date()).getTime();
    
                 {/* 4. Realiza la petición asicrónica cuando: 
                     (1) La estampa de tiempo de expiración (expiringTime) es nula, o  
                     (2) La estampa de tiempo actual es mayor al tiempo de expiración */}
    
                 if(expiringTime === null || nowTime > parseInt(expiringTime)) {
    
                     {/* 5. Request */}
    
                     let API_KEY = "AQUÍ VA SU API KEY DE OPENWEATHERMAP"
                     let response = await fetch(`https://api.openweathermap.org/data/2.5/forecast?q=Guayaquil&mode=xml&appid=${API_KEY}`)
                     savedTextXML = await response.text();
    
    
                     {/* 6. Diferencia de tiempo */}
    
                     let hours = 1
                     let delay = hours * 3600000
    
    
                     {/* 7. En el LocalStorage, almacena texto en la clave openWeatherMap y la estampa de tiempo de expiración */}
    
                     localStorage.setItem("openWeatherMap", savedTextXML)
                     localStorage.setItem("expiringTime", (nowTime + delay ).toString() )
                 }
    
                 {/* XML Parser */}
    
                 ... 
    
                 {/* Arreglo para agregar los resultados */}
    
                 ...
    
             })()
    
         },[])
    
         ...
    
  2. (STOP 3) Compruebe el resultado en el navegador.

BasicTable: Análisis del XML

  1. Defina:

    • Los datos a procesar y mostrar, p.e.: rangeHours y windDirection.
    • La estructura de datos para almacenar los datos, p.e.: Arreglo de objetos [{ ... }, { ... }].
  2. En el componente App.tsx:

    NOTA: Revise los comentarios numerados y adáptelos al código de su componente

     function App() {
    
         {/* 
             1. Agregue la variable de estado (dataTable) y función de actualización (setDataTable).
         */}
    
         let [rowsTable, setRowsTable] = useState([])
    
         ...
    
    		
         useEffect(()=>{
    
             (async ()=>{
    
                 ...
    
                 {/* Modificación de la variable de estado mediante la función de actualización */}
    
                 ...
    
                 {/* 
                     2. Procese los resultados de acuerdo con el diseño anterior.
                        Revise la estructura del documento XML para extraer los datos necesarios. 
                 */}
    
                 let arrayObjects = Array.from( xml.getElementsByTagName("time") ).map( (timeElement) =>  {
    					
                     let rangeHours = timeElement.getAttribute("from").split("T")[1] + " - " + timeElement.getAttribute("to").split("T")[1]
    
                     let windDirection = timeElement.getElementsByTagName("windDirection")[0].getAttribute("deg") + " "+  timeElement.getElementsByTagName("windDirection")[0].getAttribute("code") 
    					
                     return { "rangeHours": rangeHours,"windDirection": windDirection }
    				
                 })
    
                 arrayObjects = arrayObjects.slice(0,8)
    			
                 {/* 3. Actualice de la variable de estado mediante la función de actualización */}
    
                 setRowsTable(arrayObjects)
    
             })()
    
         },[])
    
         ...
    
         return (
    
             ...
    
             <Grid xs={12} lg={8}>
    
                 {/* 4. Envíe la variable de estado (dataTable) como prop (input) del componente (BasicTable) */}
    
                 <BasicTable rows={rowsTable}></BasicTable>
    
             </Grid>
    
             ...
    
         )
     }
    
  3. En el componente BasicTable.tsx.

    NOTA: Revise los comentarios numerados y adáptelos al código de su componente

     {/* 1. Importe los hooks de estado y ejecución en segundo plano (useState y useEffect)  */}
    	
     import { useState, useEffect } from 'react';
    	
     {/* 2. Comente la funciones de procesamiento de datos (createData) y las variables con valores fijos (rows) */}
    
     /*
         function createData(
             ...	
         }
     */
    
     /*
         const rows = [
         ...
         ];
     */
    
     {/* 3. Declare la interfaz del prop de entrada */}
    
     interface Config {
         rows: Array<object>;
     }
    
     export default function BasicTable( data:Config ) {
    
         {/* 
             4. Declare la variable de estado (rows) y la función de actualización (setRows).
             Use el mismo identificador de la variable con valores fijos (rows)
         */}
    
         let [rows, setRows] = useState([])
    		
         {/* 
             5. Agregue el hook useEffect, controlado por el prop del componente (data), y
             Dentro del hook, invoque al métdo de actualización con el valor del prop (data.rows).
         */}
    
         useEffect( () => {
    
             (()=> {
    
                 setRows(data.rows)
    
             })()
    
         }, [data] )
    
    
         {/* JSX */}
    
         return (
    
             ...
                 {/* Modifique la cabecera de la tabla con los títulos adecuados */}
    
                 <TableCell>Rango de horas</TableCell>
                 <TableCell align="right">Dirección del viento</TableCell>
             ...
    
             	{/* Modifique las filas de la tabla con las claves rangeHours y windDirection del objeto  */}
                 {rows.map((row) => (
                     <TableRow
                         key={row.rangeHours}
                         ...
                     >
                         <TableCell component="th" scope="row">
                             {row.rangeHours}
                         </TableCell>
                         <TableCell align="right">{row.windDirection}</TableCell>
                     </TableRow>
                 ))}
             ...
    
         )
     }
    
  4. (STOP 4) Compruebe el resultado en el navegador.
  5. Versiona local y remotamente el repositorio dashboard.
  6. Despliega la aplicación dashboard.

Documentación

Fundamental

Términos

React Lifecycle, React Lifecycle phases, parser

Referencias