Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
June 18, 2022 12:35 am GMT

Haciendo un fetching de datos y creando un custom hook.

Tabla de contenido

Introduccin

Tecnologas a utilizar

Creando el proyecto

Primeros pasos

Haciendo nuestro primer fetch

Mostrando los datos de la API en pantalla

Creando un custom hook

Mejorando el hook useFetch

Agregando ms componentes y refactorizando

Header.tsx

Loading.tsx

ErrorMessage.tsx

Card.tsx

LayoutCards.tsx

Introduccin

El propsito de este post es ensear una manera de como realizar peticiones HTTP de tipo GET mediante el uso de React y un custom hook.

Nota: Este post requiere que sepas las bases de React (hooks bsicos y peticiones con fetch).

Cualquier tipo de Feedback es bienvenido, gracias y espero disfrutes el articulo.

Tecnologas a utilizar.

React JS (version 18)

Vite JS

TypeScript

Rick and Morty API

CSS vanilla (Los estilos los encuentras en el repositorio al final de este post)

Creando el proyecto.

npm init vite@latest

En este caso le colocaremos de nombre: fetching-data-custom-hook (opcional).

Seleccionaremos React y luego TypeScript.

Luego ejecutamos el siguiente comando para navegar al directorio que se acaba de crear.

cd fetching-data-custom-hook

Luego instalamos las dependencias:

npm install

Despus abrimos el proyecto en un editor de cdigo (en mi caso VS code)

code .

Primeros pasos.

Dentro de la carpeta src/App.tsx borramos todo el contenido del archivo y colocamos un componente funcional que muestre un titulo y un subtitulo.

const App = () => {  return (            <h1 className="title">Fetching data and create custom Hook</h1>      <span className="subtitle">using Rick and Morty API</span>  )}export default App;

Mostrando titulo y subtitulo

Antes que nada, crearemos un par de interfaces que nos ayudaran al auto completado de las propiedades que viene en la respuesta JSON que nos proporciona la API.

  • La primera interfaz Response contiene la propiedad results que es un arreglo de Results.
  • La segunda interfaz Result, solo contiene 3 propiedades (aunque hay ms, puedes revisar la documentacin de la API), seleccione un ID, el nombre y la imagen del personaje.
interface Response {  results: Result[]}interface Result {  id: number;  name: string;  image: string;}

Haciendo nuestro primer Fetch.

  1. Primero agregamos un estado que sea de tipo Result[] y el valor por defecto sera un arreglo vaco ya que aun no hacemos la llamada a la API. Esto nos servir para almacenar los datos de la API y poder mostrarlos.
const App = () => {  const [data, setData] = useState<Result[]>([]);  return (        <h1 className="title">Fetching data and create custom Hook</h1>      <span className="subtitle">using Rick and Morty API</span>  )}export default App;
  1. Para poder realizar un fetch de datos, tenemos que hacerlo en un useEffect, ya que necesitamos ejecutar el fetch cuando nuestro componente se renderice por primera vez.

Como necesitamos que solo se ejecuta una vez, colocamos un arreglo vaci (o sea, sin dependencia alguna).

const App = () => {  const [data, setData] = useState<Result[]>([]);    useEffect(()=> {    },[]) // arreglo vaci  return (    <div>      <h1 className="title">Fetching data and create custom Hook</h1>      <span className="subtitle">using Rick and Morty API</span>    </div>  )}export default App;
  1. Dentro del cuerpo de la funcin del useEffect, se har la llamada a la API, y como el useEffect no nos permite usar cdigo asncrono directamente, haremos la llamada mediante promesas por mientras.
const [data, setData] = useState<Result[]>([]);useEffect(()=> {   fetch('https://rickandmortyapi.com/api/character/?page=8')   .then( res => res.json())   .then( (res: Response) => {})   .catch(console.log)   },[])
  1. Una vez resuelta las promesas, obtendremos la data correspondiente a la API, la cual la colocaremos al estado mediante la funcin setData

Con esto ya podramos mostrar los datos en pantalla.

Si algo sale mal con la API, el catch se encargara de atrapar el error y mostrarlo por consola y el valor del estado data se queda como arreglo vaci (y al final no se mostrara nada mas que el titulo y subtitulo de la app).

const [data, setData] = useState<Result[]>([]);useEffect(()=> {   fetch('https://rickandmortyapi.com/api/character/?page=8')   .then( res => res.json())   .then( (res: Response) =>  {      setData(res.results);   })   .catch(console.log)   },[])

Mostrando los datos de la API en pantalla.

Antes de mostrar los datos de la API, debemos hacer una evaluacin.

Solo si la longitud del valor del estado data es mayor a 0, mostramos los datos de la API en pantalla

Si la longitud de del valor del estado data es menor o igual a 0, no se mostrara ningn dato en pantalla, solamente el titulo y subtitulo.

const App = () => {    const [data, setData] = useState<Result[]>([]);    useEffect(()=> {       fetch('https://rickandmortyapi.com/api/character/?page=8')       .then( res => res.json())       .then( (res: Response) =>  {          setData(res.results);       })       .catch(console.log)       },[])  return (    <div>      <h1 className="title">Fetching data and create custom Hook</h1>      <span className="subtitle">using Rick and Morty API</span>      {        (data.length > 0) && <p>data</p>      }    </div>  )}export default App;

Ahora, una vez confirmado que si tenemos datos en el valor del estado data, proseguiremos a mostrar y moldear los datos.

Mediante la funcin map que se usa en arreglos. Recorremos el arreglo del valor del estado data y retornaremos un nuevo componente JSX que en este caso solo sera una imagen y un texto.

NOTA: la propiedad key dentro del div, es un identificador que usa React en las listas, para renderizar los componentes de forma ms eficiente. Es importante colocarla.

const App = () => {    const [data, setData] = useState<Result[]>([]);    useEffect(()=> {       fetch('https://rickandmortyapi.com/api/character/?page=8')       .then( res => res.json())       .then( (res: Response) =>  {          setData(res.results);       })       .catch(console.log)       },[])  return (    <div>      <h1 className="title">Fetching data and create custom Hook</h1>      <span className="subtitle">using Rick and Morty API</span>      {        (data.length > 0) && data.map( ({ id, image, name }) => (          <div key={id}>             <img src={image} alt={image} />             <p>{name}</p>           </div>        ))      }    </div>  )}export default App;

De esta forma hemos acabado de realizar un fetching de datos y mostrarlos en pantalla correctamente. Pero aun podemos mejorarlo.

Mostrando cards

Creando un custom hook.

Dentro de la carpeta src/hook creamos un archivo llamado useFetch.

Creamos la funcin y cortamos la lgica del componente App.tsx

const App = () => {  return (    <div>      <h1 className="title">Fetching data and create custom Hook</h1>      <span className="subtitle">using Rick and Morty API</span>      {        (data.length > 0) && data.map( ({ id, image, name }) => (          <div key={id}>             <img src={image} alt={image} />             <p>{name}</p>           </div>        ))      }    </div>  )}export default App;

Pegamos la lgica dentro de esta funcin, y al final retornamos el valor del estado data.

export const useFetch = () => {  const [data, setData] = useState<Result[]>([]);  useEffect(()=> {     fetch('https://rickandmortyapi.com/api/character/?page=8')     .then( res => res.json())     .then( (res: Response) =>  {        setData(res.results);     })     .catch(console.log)     },[]);  return {    data  }}

Por ultimo, hacemos la llamada al hook useFetch extrayendo la data.

Y listo, nuestro componente queda aun ms limpio y fcil de leer.

const App = () => {  const { data } = useFetch();  return (    <div>      <h1 className="title">Fetching data and create custom Hook</h1>      <span className="subtitle">using Rick and Morty API</span>      {        (data.length > 0) && data.map( ({ id, image, name }) => (          <div key={id}>             <img src={image} alt={image} />             <p>{name}</p>           </div>        ))      }    </div>  )}export default App;

Pero espera, aun podemos mejorar este hook.

Mejorando el hook useFetch.

Ahora lo que haremos sera mejorar el hook, agregando ms propiedades.

Al estado existente le agregaremos otras propiedades y este nuevo estado sera del tipo DataState

interface DataState {    loading: boolean;    data: Result[];    error: string | null;}

loading, valor booleano, nos permitir saber cuando se este haciendo la llamada a la API. Por defecto el valor estar en true.

error, valor string o nulo, nos mostrar un mensaje de error. Por defecto el valor estar en null.

data, valor de tipo Result[] , nos mostrara la data de la API. Por defecto el valor ser un arreglo vaci.

NOTA: se acaba de renombrar las propiedades del estate

data dataState

setData setDataState

export const useFetch = () => {    const [dataState, setDataState] = useState<DataState>({      data: [],      loading: true,      error: null  });  useEffect(()=> {     fetch('https://rickandmortyapi.com/api/character/?page=8')     .then( res => res.json())     .then( (res: Response) =>  {        setData(res.results);     })     .catch(console.log)     },[]);  return {    data  }}

Ahora sacaremos la lgica del useEffect en una funcin aparte. dicha funcin tendr el nombre de handleFetch.

Usaremos useCallback, para memorizar esta funcin y evitar que se vuelva a crear cuando el estado cambie.

El useCallback tambin recibe un arreglo de dependencias, en este caso lo dejaremos vaci ya que solo queremos que se genere una vez.

const handleFetch = useCallback(    () => {},    [],)

La funcin que recibe en useCallback, puede ser asncrona por lo cual podemos usar async/await.

  1. Primero colocamos un try/catch para manejar los errores.
  2. Luego creamos una constante con el valor de la URL para hacer la llamada a la API.
  3. Hacemos la llamada a la API usando fetch y le mandamos la URL (el await nos permitir esperar una respuesta ya sea correcta o errnea, en caso de error, se ira directo a la funcin catch).
const handleFetch = useCallback(      async () => {          try {                            const url = 'https://rickandmortyapi.com/api/character/?page=18';              const response = await fetch(url);          } catch (error) {}        },        [],    )
  1. Despus evaluamos la respuesta, si hay un error entonces activamos el catch y mandamos el error que nos da la API.
  2. En el catch, vamos a settear el estado. Llamamos al setDataState, le pasamos una funcin para obtener los valores anteriores (prev). Retornamos lo siguiente.
    1. Esparcimos las propiedades anteriores (prev), que en este caso solo sera el valor de la propiedad data, que terminara siendo un arreglo vaci.
    2. loading, lo colocamos en false.
    3. error, casteamos el valor del parmetro error que recibe el catch para poder obtener el mensaje y colocarlo en esta propiedad.
const handleFetch = useCallback(      async () => {          try {                            const url = 'https://rickandmortyapi.com/api/character/?page=18';              const response = await fetch(url);              if(!response.ok) throw new Error(response.statusText);          } catch (error) {              setDataState( prev => ({                  ...prev,                  loading: false,                  error: (error as Error).message              }));          }        },        [],    )
  1. Si no hay error por parte de la API, obtenemos la informacin, y setteamos el estado de una manera similar a como lo hicimos en el catch.
  2. Llamamos al setDataState, le pasamos una funcin para obtener los valores anteriores (prev). Retornamos lo siguiente.
    1. Esparcimos las propiedades anteriores (prev), que en este caso solo sera el valor de la propiedad error que terminara siendo un nulo.
    2. loading, lo colocamos en false.
    3. data, sera el valor de la contante dataApi accediendo a su propiedad results.
const handleFetch = useCallback(      async () => {          try {                            const url = 'https://rickandmortyapi.com/api/character/?page=18';              const response = await fetch(url);              if(!response.ok) throw new Error(response.statusText);              const dataApi: Response = await response.json();              setDataState( prev => ({                  ...prev,                  loading: false,                  data: dataApi.results              }));          } catch (error) {              setDataState( prev => ({                  ...prev,                  loading: false,                  error: (error as Error).message              }));          }        },        [],    )

Despus de crear la funcin handleFetch, nos regresamos al useEffect al cual le quitamos la lgica y agregamos lo siguiente.

Evaluamos si el valor del estado dataState accediendo a la propiedad data, contiene una longitud igual a 0, entonces queremos que se ejecute la funcin . Esto es para evitar que se llame ms de una vez dicha funcin.

useEffect(() => {    if (dataState.data.length === 0) handleFetch();}, []);

Y el hook quedara de esta manera:

NOTA: al final del hook, retornamos, mediante el operador spread, el valor del estado dataState.

NOTA: se movieron las interfaces a su carpeta respectiva, dentro de src/interfaces.

import { useState, useEffect, useCallback } from 'react';import { DataState, Response } from '../interface';const url = 'https://rickandmortyapi.com/api/character/?page=18';export const useFetch = () => {    const [dataState, setDataState] = useState<DataState>({        data: [],        loading: true,        error: null    });    const handleFetch = useCallback(        async () => {            try {                const response = await fetch(url);                if(!response.ok) throw new Error(response.statusText);                const dataApi: Response = await response.json();                setDataState( prev => ({                    ...prev,                    loading: false,                    data: dataApi.results                }));            } catch (error) {                setDataState( prev => ({                    ...prev,                    loading: false,                    error: (error as Error).message                }));            }        },        [],    )    useEffect(() => {        if (dataState.data.length === 0) handleFetch();    }, []);    return {        ...dataState    }}

Antes de usar las nuevas propiedades de este hook, haremos una refactorizacin y crearemos ms componentes.

Agregando ms componentes y refactorizando.

Lo primero es crear una carpeta components dentro de src.

Dentro de la carpeta componentes creamos los siguientes archivos.

Header.tsx

Dentro de este componente solo estarn el titulo y el subtitulo anteriormente creados.

export const Header = () => {    return (        <>            <h1 className="title">Fetching data and create custom Hook</h1>            <span className="subtitle">using Rick and Morty API</span>        </>    )}

Loading.tsx

Este componente solo se mostrar si la propiedad loading del hook esta en true.

export const Loading = () => {  return (    <p className='loading'>Loading...</p>  )}

Mostrando el loading

ErrorMessage.tsx

Este componente solo se mostrar si la propiedad error del hook contiene un valor string.

export const ErrorMessage = ({msg}:{msg:string}) => {  return (    <div className="error-msg">{msg.toUpperCase()}</div>  )}

Mostrando el error

Card.tsx

Muestra los datos de la API, o sea la imagen y su texto.

import { Result } from '../interface';export const Card = ({ image, name }:Result) => {    return (        <div className='card'>            <img src={image} alt={image} width={100} />            <p>{name}</p>        </div>    )}

LayoutCards.tsx

Este componente sirve como contenedor para hacer el recorrido de la propiedad data y mostrar las cartas con su informacin.

NOTA: usamos memo, encerrando nuestro componente, con el propsito de evitar re-renders, que probablemente no se noten en esta aplicacin pero solo es un tip. Dicha funcin memo, solo se vuelve a renderizar si la propiedad data cambia sus valores.

import { memo } from "react"import { Result } from "../interface"import { Card } from "./"interface Props { data: Result[] }export const LayoutCards = memo(({data}:Props) => {    return (        <div className="container-cards">            {                (data.length > 0) && data.map( character => (                    <Card {...character} key={character.id}/>                ))            }        </div>    )})

As quedara nuestro componente App.tsx

Creamos una funcin showData, y evaluamos:

  • Si la propiedad loading es true, retornamos el componente <Loading/>
  • Si la propiedad error es true, retornamos el componente <ErrorMessage/> , mandando el error al componente.
  • Si ninguna de las condiciones se cumple, significa que los datos de la API ya estn listos y se retornamos el componente <LayoutCards/> y le mandamos la data para que la muestre.

Finalmente, debajo del componente , abrimos parntesis y hacemos la llamada a la funcin showData.

import { ErrorMessage, Header, Loading, LayoutCards } from './components'import { useFetch } from './hook';const App = () => {  const { data, loading, error } = useFetch();  const showData =  () => {    if (loading) return <Loading/>    if (error) return <ErrorMessage msg={error}/>    return <LayoutCards data={data} />  }  return (    <>      <Header/>      { showData() }    </>  )}export default App;

NOTA: Tambin puedes mover la funcin showData al hook, y cambiar la extension del archivo del hook a .tsx, esto es porque se esta utilizando JSX al retornar diversos componentes.

Gracias por llegar hasta aqu.

Te dejo el repositorio para que le eches un vistazo si quieres.

GitHub logo Franklin361 / fetching-data-custom-hook

Tutorial on how to fetching data and creating a custom hook

Fetching data and create custom Hook

Tutorial on how to fetching data and creating a custom hook

demo

Link to tutorial post


Original Link: https://dev.to/franklin030601/hacer-un-fetching-de-datos-y-crear-un-custom-hook-4bjh

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To