No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Consumiendo la FakeStore API para pintar cards

8/31
Recursos

Aportes 61

Preguntas 7

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

La API de Platzi no es estable y eso puede traerte resultados inesperados como imágenes sin relación al producto, poco o demasiados datos, etc. Yo les aconsejo usar la API de Fake Store. Es muy similar.
Para las imágenes, eliminen únicamente la posición ([0]). En cuanto al título de los productos, si les causa ruido que sean muy extensos, pueden agregar la propiedad truncate de Tailwind para agregar elipsis. También puedes agregar un mr-2 (margin-right) y quedará excelente.

Seamos un poco más profesionales 😉

Creemos un archivo donde vivirá la url de la api en .src/api/index.js:

export const apiUrl = 'https://api.escuelajs.co/api/v1'

Usemos async y await y SIEMPRE usemos try catch en peticiones GET:

import { useState, useEffect } from "react"
import { Layout } from "../../Components/Layout"
import { Card } from "../../Components/Card"

import { apiUrl } from '../../api/'

export const Home = () => {
  const [Items, setItems] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(`${apiUrl}/products`)
        const data = await response.json()
        setItems(data)
      } catch (error) {
        console.error(`Oh no, ocurrió un error: ${error}`);
      }
    }
    fetchData()
  }, [])


  return (
    <Layout>
      Home
      <section className="grid gap-4 grid-cols-4 w-full max-w-screen-lg">
        {
          Items?.map(item => (
            <Card
              key={item.id}
              data={item}
            />
          ))
        }
      </section>
    </Layout>
  )
}

Aplique desestructuración

{porducts.map((product) => (
 <Card key={product.id} {...product} />
))}

Cuando un objeto no existe, React arroja un error y no se renderiza nada. Por ejemplo, si llamamos {data.data.category.name} dentro de un componente y el objeto data, data.category o data.category.name no existen, la pantalla se mostrará en blanco. No obstante, podemos evitar que se rompa la página si utilizamos la sintaxis opcional de encadenamiento de operadores de navegación segura, representada por el símbolo ?., de esta forma: {data.data?.category?.name}. En este caso, si alguno de los objetos no existe, la expresión devolverá undefined, pero el código seguirá ejecutándose sin problemas. Esto puede resultar muy útil si la API no devuelve información para algunos productos y queremos evitar que la página se rompa por completo.

Así voy con Nextjs, creo que está bonito.

Yo me armé un archivo para los endpoints y me hice un hook ‘useFetch’

import { useEffect, useState } from 'react';

export const useFetch = apiUrl => {
	const [data, setData] = useState();

	useEffect(() => {
		fetch(apiUrl)
			.then(res => res.json())
			.then(data => setData(data));
	}, [apiUrl]);

	return data;
};

El Home me queda así:

import { productsApi } from '../../Assets/ApiUrls';
import { Card } from '../../Components/Card/Card';
import { Layout } from '../../Components/Navbar/Layout/layout';
import { useFetch } from '../../Hooks/UseFetch';

export const Home = () => {
	const data = useFetch(productsApi);

	return (
		<Layout>
			{data?.map(product => (
				<Card key={product?.id} title={product?.title} category={product?.category} price={product.price} image={product.image} />
			))}
		</Layout>
	);
};

Madre mia, hace al menos un mes que dejé el curso por la mitad… Pero desde luego lo que he visto hoy no me parece serio. Le pasa una prop data a card y para recibirla utiliza data.data en vez de desestructurar props en {data} , igualmente si no quiere desestructurarla deberia de llamar a props.data por convención…

Hola comunidad, les comparto mis apuntes en Notion sobre cards y llamado a API´s, espero les sean de utilidad.
Link aquí:
https://bg99astro.notion.site/Cards-y-llamado-a-API-s-b5be45bca26e48a99a25ffb9b650e383

La fakeapi de platzi me esta dando errores, asi que use la api de fake store api, funciona bastante similar a la api de platzi, asi que no hay que hacer muchos cambios.

fake store api

Si quieren hacer su propia API con endpoints y todas las configuraciones que deseen, pueden hacerla localmente con json server y hacer el deploy con Render. Es super fácil, les dejo un tutorial, está ingles, pero funciona bien:
https://www.youtube.com/watch?v=EcxYcpF3W7c y se adjunta un repo de ejemplo: https://github.com/Md-Irfan-FullStackDeveloper/test_api
Hagan el ejemplo y luego para actualizarla solo tienen que hacer un commit y push a la misma rama, ya que render se conecta con github y en pocos minutos su url se actualiza. Sigan la estructura de la Fake API de Platzi para que no tengan errores de undefined en su código o si cambian la estructura, no se olviden de adaptar el código.
En el archivo db.json que creen:

{
  "products": [
  {
    "id": 1,
    "title": "Handmade Fresh Table",
    "price": 687,
    "description": "Andy shoes are designed to keeping in...",
    "category": {
      "id": 5,
      "name": "Others",
      "image": "https://placeimg.com/640/480/any?r=0.591926261873231"
    },
    "images": [
      "https://placeimg.com/640/480/any?r=0.9178516507833767",
      "https://placeimg.com/640/480/any?r=0.9300320592588625",
      "https://placeimg.com/640/480/any?r=0.8807778235430017"
    ]
  },
  {
    "id": 2,
    "title": "Handmade Fresh ",
    "price": 687,
    "description": "Andy shoes",
    "category": {
      "id": 5,
      "name": "Others",
      "image": "https://placeimg.com/640/480/any?r=0.591926261873231"
    },
    "images": [
      "https://placeimg.com/640/480/any?r=0.9178516507833767",
      "https://placeimg.com/640/480/any?r=0.9300320592588625",
      "https://placeimg.com/640/480/any?r=0.8807778235430017"
    ]
  },
  	.
  	. El Resto de Productos
  	.
  ],
  "categories": [
  {
    "id": 1,
    "name": "Clothes",
    "image": "https://api.lorem.space/image/fashion?w=640&h=480&r=4278"
  },
  {
    "id": 2,
    "name": "Electronics",
    "image": "https://api.lorem.space/image/fashion?w=640&h=480&r=4278"
  },
	.
	. El Resto de Categorias
	.
  ]
}

mobile-first & lazy-loading

para hacer nuestro home mobile first basta con agregar una linea de código en tailwind: gid-cols-2, quedaría nuestro div de la siguiente manera

grid gap-4  grid-cols-2 sm:grid-cols-4 w-full max-w-screen-lg px-2

ahora para mejorar el rendimiento de nuestra aplicación deberiamos aplicar el lazy-loading, esto lo logramos de una manera muy sencilla, instalaremos el siguiente paquete
npm i react-lazy-load-image-component

  • luego en nuestra card importamos lo siguiente
import {LazyLoadImage} from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';

y reemplazamos la etiqueta img por la etiqueta LazyLoadImage incluyendo un height, un width y un effect, quedando de la siguiente manera

<LazyLoadImage className='w-full h-full object-cover rounded-lg'
             src={data.data.url} alt={data.data.name} effect='blur' height={'100%'} width={'100%'} />

dentro de los efectos tienes blur, black-and-white y opacity, usa el que desees y no olvides importarlo de css como lo hicimos con blur, de esta manera ya tenemos aplicado el lazy-load en nuestro componente card y hemos mejorado el performance de nuestra aplicación, así como tambien hemos aplicado la buena tecnica del mobile-first

En caso de que quieran hacer el grid full responsive usen las siguientes clases de tailwindcss

<section className='grid place-items-center gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 w-full max-w-screen-xl'>
      {items?.map((item) => (
        <Card key={item.id} item={item} />
      ))}
    </section>

Una aclaración sobre el array que se pasa como segundo argumento (array de dependencias) al useEffect:
Este array le dice a React qué variables debe observar para determinar si el efecto debe ejecutarse nuevamente. Si alguna de las variables dentro del array de dependencias cambia su valor, React ejecutará el efecto nuevamente. Si las variables no cambian, React omitirá la ejecución del efecto.

Algo que no me quedó claro es, por qué en el min 18:56 se debe introducir

data.data.category.name 

para obtener el nombre del artículo, osea, por qué dos veces data?

Usando la API de Fake Store debido a problemas con la de Platzi, asi me quedo.

Nose porque no me esta recorriendo el map. solo me muestra un producto.

si ponene le data dentro de corchetes no deben usar el data.data

const Card = ({data}) => {
 return (
  <div className='bg-white cursor-pointer w-56 h-60 rounded-lg'>
   <figure className='relative mb-2 w-full h-4/5'>
    <span className='absolute bottom-0.5 left-0 bg-white/60 rounded-lg text-black text-xs ml-1 px-1'>{data.category.name}</span>
    <img className='w-full h-full object-cover rounded-lg' src={data.images[0]} alt={data.title} />
    <div className='absolute top-0.5 right-0.5 flex justify-center items-center bg-white rounded-full w-5 h-5 font-bold pb-0.5'>+</div>
   </figure>
   <span className='flex justify-between'>
    <p className='text-sm font-light'>{data.title}</p>
    <p className='text-sm font-medium'>{'$'+data.price}</p>
   </span>
  </div>
 );
};

Personalmente le agregue un Loader de la libreria React Spinners (https://mhnpd.github.io/react-loader-spinner/docs/intro). Por si hay un retraso en la llamada a la API.

useState

Muy buena explicación de API.

Así vamos…

Hice un Loading Skeleton de la Card, dejo el link de la previsualización

Soy mas apegado a usar async/await en vez de promises. Así quedo el useEffect en mi código.

  useEffect(() => {
    const getProducts = async () => {
      try {
        const data = await fetch('https://api.escuelajs.co/api/v1/products')
        const jsonData = await data.json()
        setItems(jsonData)
      } catch (error) {
        console.log(error)
      }
    }
    getProducts()
  }, [])

La API de platzi tiene muchos problemas con las imágenes. Les recomiendo que hagan el curso usando esta API: https://fakestoreapi.com/

Fíjense en la documentación de la API para que sepan las cosas que cambian al momento de consumir y renderizar las propiedades del objeto que nos devuelve.

https://dummyjson.com/ Es una muy buena alternativa a la api de platzi, la recomiendo, esta muy completa!.

Para organizar todo mejor cree un custom hook llamado “useFetch”. No lo hice yo, lo saqué de https://javascript.plainenglish.io/react-creating-usefetch-custom-hook-d123ebfd5ff.

Además hice un contexto llamado “ProductsContext” el cual usa este hook.

creo que deberían quitarle la opción a la fakeapi de eliminar los productos, porque probando el api con postman solo existe un producto en el momento en que estoy viendo el curso, al parecer vandalizan mucho este api

Esta es version trabajada con typescript-

Home

import {useState, useEffect} from "react"
import { Layout } from "../../Components/Layout"
import { Card } from "../../Components/Card"

interface MyData {
  id: number;
  title: string;
  price: number;
  description: string;
  category: {
    id: number
    name: string
    image: string
  }
  images: string[]
}

const Home = () => {
  const [items, setItems] = useState<MyData[]>([])

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.escuelajs.co/api/v1/products')
        const data = await response.json()
        setItems(data)
      } catch(error) {
        console.error(error);
      }
    }
    fetchData()
  }, [])
  return (
    <Layout>
      Home
      {items?.map((item) => (
        <Card key={item.id} {...item}/>
      ) )}
      
    </Layout>
  )
}

export {Home}

card

interface MyData {
  id: number;
  title: string;
  price: number;
  description: string;
  category: {
    id: number
    name: string
    image: string
  }
  images: string[]
}


const Card = (data: MyData) => {
  return (
    <div className='bg-white cursor-pointer w-56 h-60 rounded-lg'>
        <figure className='relative mb-2 w-full h-4/5'>
            <span className="absolute bottom-0 left-0 m-2 bg-white/60 rounded-lg text-black text-xs px-3 py-0.5">
              {data.category.name}
            </span>
            <img className="w-full h-full object-cover rounded-lg" 
            src={data.images[0]} alt={data.title} />
            <div className="absolute top-0 right-0 flex justify-center items-center bg-white m-2 w-6 h-6 rounded-full">
                +
                </div>
        </figure>
        <p className="flex justify-between">
            <span className="text-sm font-light">{data.title}</span>
            <span className="text-lg font-medium">${data.price}</span>
        </p>
    </div>
  )
}

export {Card}

Hola, ya solo hay 2 productos como respuesta del API

![](https://static.platzi.com/media/user_upload/image-b3ab3eee-5a7b-4e39-aab5-58b8386d882b.jpg)
Pregunta ¿puedo tener almacenados los productos en un servicio como firebase? y de ser así, ¿cómo se conecta?
Es mejor deconstruir los props de para que en Card no tengas que hacer data.data, eso se hace fácil desde el mismo componentes con `const Card({data}) => {}` Es más natural leerlo así

Yo personalmente trabaje las clases del div que contiene las cartas ya que al hacer resize o verse en diferentes dispositivos las cartas se sobreponen una encima de otra y demás.
Para ello genere un archivo index.css para el Home el cual contiene el siguiente código.

.container_cards{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
    gap: 30px;
    width: 95%;
    justify-items: center;
}

Esto hace que no tengamos que usar un media-query para ir cambiando las columnas que tienen que generarse, sino que depende de la pantalla estas se acomodoran automaticamente.

Ahora al hacer esto toca pensar que el navbar tenemos que modificarlo si también queremos que se vea bien desde un dispositivo mas pequeño.

ome un hook personalizado que publico compañero y lo integre, este recibe un parámetro que es la url del endponit

import { useEffect, useState } from 'react';
export const useFetch = (url) => {
	const [data, setData] = useState();
	useEffect(() => {
		fetch(url)
			.then(res => res.json())
			.then(data => setData(data));
	}, [url]);

	return data;
};


cree un archivo para la ruta de la api y poder acceder a los diferentes endpoint

// Definí UrlApi como un objeto en lugar de un array. Esto permitirá acceder a UrlApi.getProduct de manera adecuada.
const UrlApi = {
    getProduct: 'https://fakestoreapi.com/products',
};
export default UrlApi;

Asi Vamos, con la Fake Store API

Me quedó asi:


Buenas, queria compartir mi callback del useEffect que lo hice un poco distinto. A mi me gusto mas de esta forma:

  useEffect(() => {
    const getProducts = async () => {
      const response = await fetch('https://api.escuelajs.co/api/v1/products');
      const data = await response.json();
      
      setItems(data);
      console.log(items);
    };
    getProducts();
  }, []);

La API de FakeStore no es disponible 😑

Hola la API de platzi no ha estado funcionando bien. Puedes usar este pequeño servidor si gustas para consumir una API desde tu propia computadora.
https://github.com/FranciscoJSB12/Ecommerce-backend
Te dejé un readme con las instrucciones para usarlo, así podrás seguir con tu curso normalmente, son bastantes sencillas.

Para que quede un poco más estético pueden usar la destructuracion

const Card = ({ data: { title, price, category, images } }) => {
  

Preferí usar async await y try catch por recomendación de un compañero, ya que es lo que se recomienda al usar APIs😅

Pero también me pareció buena idea para practicar y repasar asincronismo en JavaScript
.
Algo que se me ocurrió fue hacer un condicional ternario que muestre los productos en función de si existen o no.
![](

Estoy armando una pricing page, en este caso no use el API de platzi, sin embargo le paso los datos de la siguiente manera


const data_princing_cards = [
  {
  'id':1,
  'plan':'Free',
  'benefits':['Benefit 1', 'Benefit 2', 'Benefit 3'],
  'price':10,
  },
  {
  'id':2,
  'plan':'Hobby',
  'benefits':['Benefit 1',  'Benefit 3'],
  'price':20,
  },
  {
  'id':3,
  'plan':'Standard',
  'benefits':['Benefit 1', 'Benefit 2', 'Benefit 3'],
  'price':30,
  },

]

function Pricing() {
  const [items, setItems] = useState(null)

  useEffect(() => {
    setItems(data_princing_cards)
  }, [])

Que mal, no me salen los 200 productos 😭😔

Yo llevo mi ecommerce así, pero con flexbox.
No sé por qué se me dificulta más con grid.

Esto es asombroso!

Hay problemas con la API, no sale bien la informacion, la imagen no corresponde a la informacion y en la mitad de la pagina ya no sale informacion

Para las personas que les gusta usar Async/Await

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

    fetchProduct();
  }, []);

Que hermoso es TailwindCSS, hay algún curso?

Para usar axios solo debemos importarlo
npm install axios y luego usarlo en el useEffect
useEffect(() => {
const apiUrl = ‘https://tu-url-de-api.com/’;

axios.get(`${apiUrl}activos`)
  .then(response => {
    setAssets(response.data);
  })
  .catch(error => {
    console.error('Error fetching assets:', error);
  });

}, []);

Este curso me tiene muy contento, nunca habia consumido una API tan rapido y entendiendo tanto !!

Una pena que la api la estén modificando constantemente entorpeciendo el resultado final. Literalmente tuve que buscar una api externa para poder seguir

)

No entiendo porque los productos son tan random… aveces inapropiados para presentar este proyecto como parte de mi portafolio. Habra alguna forma de arreglarlo? Una solución que pienso ahora para mi proyecto es agregar productos a una categoria creada por mi.
. Alguien tiene alguna recomendación de como solucionar esto?
.
Imagen de categoria electronicos.

.
Detalle de producto

.
Productos agregados al carrito

Card component

const Card = ({ product }) => {
  const { category, images, title, price } = product

  return (
    <div className="bg-white cursor-pointer w-56 h-60 rounded-lg">
      <figure className="relative mb-2 w-full h-4/5">
        <span className="absolute bottom-0 left-0 bg-white/60 rounded-lg text-black text-xs m-2 px-3 py-0.5">
          {category.name}
        </span>
        <img className="w-full h-full object-cover rounded-lg" src={images[0]} alt={title} />
        <div className="absolute top-0 right-0 flex justify-center items-center bg-white w-6 h-6 rounded-full m-2  p-1">
          +
        </div>
      </figure>
      <p className="flex justify-between">
        <span className="text-sm font-light">{title}</span>
        <span className="text-lg font-medium">${price}</span>
      </p>
    </div>
  )
}

export default Card

Quedó genial! por ahora solo me trae 20 elementos !

https://api.escuelajs.co/api/v1/products