No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Aprende todo un fin de semana sin pagar una suscripci贸n 馃敟

Aprende todo un fin de semana sin pagar una suscripci贸n 馃敟

Reg铆strate

Comienza en:

2D
13H
47M
51S
Curso Pr谩ctico de React.js

Curso Pr谩ctico de React.js

Oscar Barajas Tavares

Oscar Barajas Tavares

Custom Hooks para la tienda

20/30
Recursos

Aportes 34

Preguntas 12

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Si est谩s aprendiendo React no es recomendable que uses el autocompletado de VSC o Github Copilot, permitele a tu memoria muscular adaptarse a la sintaxis y l贸gica, luego que lo domines a la perfecci贸n siente libre de usarlos para ahorrar tiempo de escritura.

Recomiendo a帽adirle un estado de 鈥渓oading鈥 al custom hook:

export default function useGetProducts(API) {
  const [products, setProducts] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    fetch(API)
      .then((res) => res.json())
      .then((response) => {
        setProducts(response);
        setIsLoading(false);
      });
  }, []);

  return { products, isLoading };
}

Tambi茅n se le podr铆a a帽adir un estado de error 馃

En React, podemos crear hooks por nuestra propia cuenta, donde nosotros podemos escribir toda la funcionalidad que deseamos. Ahora, haremos un hook el cual servir谩 para realizar la petici贸n a todos los productos y traer su precio, im谩gen y descripci贸n.

//useGetProducts.js
import { useEffect, useState } from 'react';
import axios from 'axios';

const useGetProducts = (API) => {
    const [products, setProducts] = useState([]);

	useEffect(async () => {
		const response = await axios(API);
		setProducts(response.data);
	}, [])

    return products;
}

export default useGetProducts;

El hook es muy sencillo. En el, creamos una array llamado products. Despu茅s con ayuda de useEffect realizamos una solicitud a una API (que es pasada como argumento), para traernos toda la informaci贸n y guardarla con ayuda de axios. setProducts (de useState) guarda el response. Al final regresamos products.

Para poder usar el custom hook, lo implementamos en ProductList

// ProductList.jsx
import useGetProducts from '@hooks/useGetProducts'; // Lo importamos

const API = 'https://api.escuelajs.co/api/v1/products';

const ProductList = () => {
	const products = useGetProducts(API);

	return (
		<section className="main-container">
			<div className="ProductList">
				{products.map(product => (
					<ProductItem product={product} key={product.id}/>
				))}
			</div>
		</section>
	);
}

Ahora, creamos una constante llamada products que ser谩 el mismo array el cual contiene toda la informaci贸n de los productos. En el, le pasamos API que ser谩 un argumento del hook. Como ya sabemos, m谩s abajo en el div, usamos el m茅todo map para el array, en donde por cada producto crear谩 una etiqueta del componente ProductItem. ProductItem recibe como datos un key, que es igual a product. id y tambi茅n product que es igual al producto del array.

Para poder aprovechar esta informaci贸n, editamos ProductItem.

import React, { useState } from 'react';
import '@styles/ProductItem.scss';
import buttonAddCart from '@icons/bt_add_to_cart.svg'

const ProductItem = ({product}) => {
	const [cart, setCart] = useState([]);

	const handleClick = () => {
		setCart([]);
	}

	return (
		<div className="ProductItem">
			<img src={product.images[0]} alt={product.title} />
			<div className="product-info">
				<div>
					<p>${product.price}</p>
					<p>{product.title}</p>
				</div>
				<figure onClick={handleClick}>
					<img src={buttonAddCart} alt="" />
				</figure>
			</div>
		</div>
	);
}

export default ProductItem;

En product item, recibimos estos datos en argumentos de la funci贸n dentro de llaves. M谩s abajo, en img, alt, y p, usamos las caracter铆sticas de cada producto que son la im谩gen, titulo, descripci贸n y precio. As铆, podemos mostrar la informaci贸n que corresponde a cada una.

Les comparto ambas formas para traer datos de una API:

  • Fetch (nativa), y con axios.

Ademas de como manejar el ciclo de vida cuando el componente va a desmontarse y el request de los datos no quede en el aire, sino que aborte dicho request.

const useGetProducts = () => {
	const [products, setProducts] = useState([]);
	const [loading, setLoading] = useState(true);
	const API = 'https://api.escuelajs.co/api/v1/products?limit=12&offset=0';

	// With fetch API
	useEffect(async () => {
      	const controller = new AbortController();
	    const { signal } = controller;
      
		try {
			const response = await fetch(API, { signal });
			const data = await response.json();

			if (data) {
				setProducts([...products, ...data]);
				setLoading(false);
			}
		} catch (err) {
			console.log('Error: ', err);
		}

		return () => controller.abort();
	}, []);

	// With axios
	useEffect(async () => {
		const cancelToken = axios.CancelToken;
		const source = cancelToken.source();

		try {
			const { data } = await axios(API, { cancelToken: source.token });

			if (data) {
				setProducts([...products, ...data]);
				setLoading(false);
			}
		} catch (err) {
			if (axios.isCancel(err)) {
				return 'axios request cancelled';
			}
			console.log('Error: ', err);
		}

		return () => source.cancel('axios request cancelled');
	}, []);

	return { products, loading };
};


cancelling-requests-with-fetch-or-axios

Para no estar repitiendo la palabra product (product.price, product.title) se puede hacer uso de la desestructuraci贸n con cada uno de los atributos

  const { id, title, price, images } = product;

Recomendacion: no usar async/await como par谩metro en useEffect, aqui un ejemplo para modificarlo
function Example() {
const [data, dataSet] = useState<any>(null)

useEffect(() => {
async function fetchMyAPI() {
let response = await fetch(鈥榓pi/data鈥)
response = await response.json()
dataSet(response)
}

fetchMyAPI()

}, [])

return <div>{JSON.stringify(data)}</div>
}

Hola chicos por si a alguno le sucede que al momento de poner la foto le da un error de que no existe la propiedad [0], la soluci贸n es:

  • product.images?.[0],

al parecer como es as铆ncrono al momento de renderizar no encuentra datos sino hasta que se resuelve toda la petici贸n por ende debemos utilizar el signo de exclamaci贸n en la propiedad images, este va a renderizar el valor solo cuando images exista dentro de product

En mi caso me gusta esta forma para desestructurar los props de react

  const { images, title, price } = product;

en la mad鈥 ahora entiendo todo lo que te dice de hooks en teoria son funciones o clases creadas en modulos, que te explico el profe que no se le entiende cuando habla鈥 jaja gracias a los que me dijeron que viera dos veces el tutorial de JS profesional

Para el fetching de datos pueden utilizar algo nativo como fetch(), ya que as铆 se libraran de librer铆as extras en su proyecto.

  const [products, setProducts] = useState([]);

  const API = "https://api.escuelajs.co/api/v1/products";
  const fetchApi = async () => {
    await fetch(API)
      .then((response) => response.json())
      .then((data) => setProducts(data));
  };

  useEffect(() => {
    fetchApi();
  }, []);

Sale un error en la consola por el uso de async dentro del useEffect(), se corrige as铆:

<import { useEffect, useState } from "react";
import axios from "axios";

const useGetProducts = (API) => {

  const [products, setProducts] = useState([]);

//se saca el fecth de los datos del UseEffec
  async function fetchData() {
    const response = await axios(API);
    setProducts(response.data);
  }

  useEffect(() => {
    fetchData();
  }, []);

  return products;
};

export default useGetProducts;> 

Hola chicos! las 煤ltimas versiones de React te dan un error cuando colocas el async directamente en el useEffect:

	useEffect(async()=>{
		const response = await axios(API);
		setPorducts(response.data);
	}, [])

para solucionarlo, debes crear otra funci贸n as铆ncrona y luego llamarla DENTRO del bloque del useEffect:

	useEffect(()=>{
		async function fetchData(){
			const response = await axios(API);
			setPorducts(response.data);
		}		
		fetchData()
	}, [])

T谩mbien Puedes crear la funci贸n fuera del bloque y luego llamarla dentro:

	async function fetchData(){
		const response = await axios(API);
		setPorducts(response.data);
	}		

	useEffect(()=>{
		fetchData()
	}, [])

Espero tengan un gran dia! el curso esta incre铆ble!! 鉂わ笍

Gente, por cierto, si no quieren usar axios pueden usar fetch tranquilamente:

import { useEffect, useState } from "react";

const useGetProducts = (API) => {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    async function fetchData() {
      const response = await fetch(API)
      const data = await response.json()
      setProducts(data)
    }
    fetchData()
  }, [])
  return products
}

export { useGetProducts }

No me aparec铆an los datos de la api pero si me hacia el mapeo
Mi error fue no poner los corchetes al llamar product, no me percate de eso y estuve batallando una hora con eso

Comparto por si a alguien le pasa algo similar 馃槄
Ya funcionando este fue uno de los art铆culos que mas me dio risa

mi practica:

import React from 'react';
import ProductItem from '@components/ProductItem';
import '@styles/ProductList.scss';
import useGetProducts from "@hooks/useGetProducts";

const API = 'https://api.escuelajs.co/api/v1/products';

const ProductList = () => {
    const products = useGetProducts(API);
    return (
        <section className="main-container">
            <div className="ProductList">
                {products.map(product => (
                    <ProductItem key={product.id} product={product}/>
                ))}
            </div>
        </section>
    );
}

export default ProductList;

import {useEffect, useState} from 'react';
import axios from "axios";

const useGetProducts = (API) => {
    const [products, setProducts] = useState([]);
    useEffect(async () => {
        const response = await axios.get(API + '?limit=15&offset=0');
        setProducts(response.data);
    }, []);
    return products;
}

export default useGetProducts;
import React, {useState} from 'react';
import '@styles/ProductItem.scss';
import btAddToCart from '@icons/bt_add_to_cart.svg';

const ProductItem = ({product}) => {
    const {id, title, price, images} = product;
	const [cart, setCart] = useState([]);
	const addToCart = () => {
		setCart([]);
	}
    return (
        <div className="ProductItem">
            <img
                src={images[0]}
                alt={title}/>
            <div className="product-info">
                <div>
                    <p>${price}</p>
                    <p>{title}</p>
                </div>
                <figure onClick={addToCart}>
                    <img src={btAddToCart} alt=""/>
                </figure>
            </div>
        </div>
    );
}

export default ProductItem;

Una forma de darle formato al precio de nuestro productos.

<p>
	{ new Intl.NumberFormat('en-US', 
		{ 
			style: 'currency', 
			currency: 'USD'
		}
	).format(product.price)}
</p> 

Me distraje viendo si pod铆a hacer un filtrado y que solo tomara la cantidad de fotos que tu quieras, ya que lo hice, lo comparto.

Otra forma como podemos completar la key,
es con la opci贸n: index, que tiene el .map como segundo argumento de la funci贸n callback

key={index}

parceros tengo un problema, no me cargan las im谩genes, los t铆tulos y precios si, pero las im谩genes no, solo me aparecen las ultimas 3 im谩genes y en consola me sale:
Failed to load resource: the server responded with a status of 404 (Not Found)

watch:1 Failed to load resource: the server responded with a status of 502 ()

me aparecen todas las im谩genes con ese error 502, agradezco si alguien puede ayudarme, llevo una hora y media d谩ndole vueltas y no encuentro soluci贸n 馃槮

Hice este hook para dar el formato de pesos colombianos

<code> 
const useNumberFormat = ({
  value = 0,
  locales = 'es-CO',
  style = 'currency',
  currency = 'COP',
} = {}) => {
  const numberFormat = new Intl.NumberFormat(locales, { style, currency }).format(value)

  return { numberFormat }
}

export {
  useNumberFormat
}

podemos usar la destructuracion en nuestro productItem, asi sabemos en el inicio de nuestro componente cuales son los valores que estamos usando, y ademas mejoramos la legibilidad de nuestro codigo al no estar usando recurrentemente la palabra product.propiedad

鉂わ笍

Algo que me agrada bastante de los custom hooks es la versatilidad de hacer l贸gica compleja en un archivo y en el jsx tener pr谩cticamente s贸lo UI, lamentablemente en la industria hay papiros atascados de l贸gica sin orden con UI anidad, valoren esta clase es ORO

Estoy notando que en la api actualmente ya no tiene url en el array de images, por lo que me parece que es la raz贸n por la cual ya no renderiza las im谩genes.

AVISO
En el index.js de nuestra aplicaci贸n deber铆amos crear la aplicaci贸n en las nuevas versiones de React de la siguiente manera:

import React from "react";
import { createRoot } from "react-dom/client";
import App from "./routes/App.jsx";

const container = document.getElementById("app");
const root = createRoot(container);

root.render(<App />);

Madre m铆a con el Copilot de github鈥 ajja

La consola da un warning con el useEffect y recomienda usarlo de la siguiente manera

useEffect(() => {
    async function fetchData() {
      const response = await axios(API);
      setProducts(response.data);  
    }
    fetchData();
  }, []);

Title se pronuncia como 藞t墨dl

En la parte de Product item se puede hacer destructuraci贸n del objeto product para hacer el c贸digo mas legible:

const ProductItem = ({ product }) => {
	const { title, price, images } = product;
	...
  return (
    <div className="ProductItem">
      <img src={images[0]} alt={title} />
      <div className="product-info">
        <div>
          <p>${price}</p>
          <p>{title}</p>
        </div>
        <figure onClick={handleClick}>
          <img src={addCart} alt="" />
        </figure>
      </div>
    </div>
  );
};

este profesor va mas r谩pido que mejor dicho c贸janlo si pueden

Yo no veo un pollo xd

Tambien podemos coger la imagen que esta destinada a la categoria, porque si se fijan, en el API tenemos un objeto que tiene un key de nombre 鈥渃ategory鈥 entonces ese category tiene la imagen del producto directamente. de ahi podemos coger la imagen.

{
    "id": 1,
    "title": "Small Granite Chicken",
    "price": 810,
    "description": "New range of formal shirts are designed keeping you in mind. With fits and styling that will make you stand apart",
    "category": {
      "id": 3,
      "name": "Furniture",
      "image": "https://placeimg.com/640/480/any?r=0.5210008225163332"
    },
    "images": [
      "https://placeimg.com/640/480/any?r=0.006060555121223832",
      "https://placeimg.com/640/480/any?r=0.1439081209827473",
      "https://placeimg.com/640/480/any?r=0.5621224244775178"
    ]
  }

El codigo cogiendo la imagen desde category quedaria algo asi similar. fijense bien que aqui tenemos src{product.category.image} en vez de
src{product.image[0]} que seria lo mismo pero en el segundo caso estamos cogiendo la primera imagen del arreglo de imagenes que tenemos en nuestro objeto.

const ProductItem = ({product}) => {
  const [cart, setCart] = useState([])
  const handleClick = () =>{
    setCart([])
  }
  return (
    <div className="ProductItem">
      <img
        src={product.category.image}
        alt={product.title}
      />
      <div className="product-info">
        <div>
          <p>${product.price}</p>
          <p>{product.title}</p>
        </div>
        <figure onClick={handleClick}>
          <img src={AddToCart} alt="add cart" />
        </figure>
      </div>
    </div>
  );
};

el segundo argumento que recibe useEffect que ahora solo lo colocamos como un array vacio, hace referencia a los elementos de los que dependera el renderizado o ejecucion de esta misma funcion (useEffect), ejemplo si nuestro llamado a la api depende de un evento que sea ejecutado con un onclick, ese mismo elemento es el que debemos pasar en el array hasta ahora vacio, asi garantizamos que el useEffect solo se ejecutara cuando ese elemento cambie su valor

Las imagenes me dieron miedo perdon