Henrry Andrés Terraza Sigcho
Mi lista quedo así con el reto.
La capacidad de consumir datos de APIs externas es una habilidad fundamental para cualquier desarrollador web moderno. Mediante el uso de fetch, podemos conectar nuestras aplicaciones React con servicios externos y mostrar información dinámica a nuestros usuarios. Esta técnica nos permite crear interfaces más ricas y funcionales que se actualizan con datos reales.
¿Cómo realizar llamadas a APIs en React usando fetch?
Fetch es una API incorporada en la mayoría de los navegadores modernos que nos permite realizar peticiones HTTP. Su principal ventaja es que está basada en promesas, lo que facilita el manejo de operaciones asíncronas como la obtención de datos desde servidores externos.
Para implementar llamadas a APIs en React, seguiremos estos pasos:
Creación del componente UserList
Primero, necesitamos crear un nuevo componente que se encargará de mostrar la lista de usuarios:
// UserList.jsx import { useState, useEffect } from 'react'; const UserList = () => { const [users, setUsers] = useState([]); useEffect(() => { fetch('https://jsonplaceholder.typicode.com/users') .then(response => response.json()) .then(data => setUsers(data)) .catch(error => console.error('Error fetching data:', error)); }, []); return ( <div> <h1>Usuarios</h1> <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); }; export default UserList;
En este componente:
useState para almacenar los usuarios, inicializándolo como un array vacío.Entendiendo el hook useEffect
El hook useEffect es fundamental para manejar operaciones asíncronas en React:
useEffect(() => { // Código para el efecto secundario // Opcional: función de limpieza return () => { // Código de limpieza }; }, []); // Array de dependencias
[]: el efecto se ejecuta solo una vez, después del primer renderizado.[dep1, dep2]: el efecto se ejecuta cuando cambia alguna dependencia.En nuestro caso, usamos un array vacío porque solo queremos hacer la llamada a la API una vez, cuando el componente se monta.
Procesando la respuesta de la API
Cuando trabajamos con fetch, es importante entender el flujo de la promesa:
fetch('https://jsonplaceholder.typicode.com/users') .then(response => response.json()) // Convertir respuesta a JSON .then(data => setUsers(data)) // Usar los datos .catch(error => console.error('Error fetching data:', error)); // Manejar errores
response.json() que también devuelve una promesa.Renderizando datos dinámicos
Para mostrar los datos obtenidos de la API, utilizamos el método map para transformar cada objeto de usuario en un elemento de la interfaz:
<ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul>
Es importante proporcionar una prop key única para cada elemento de la lista. En este caso, utilizamos el id del usuario que viene directamente de la API, lo que es más eficiente que usar el índice del array.
¿Cómo integrar el componente en nuestra aplicación?
Una vez creado nuestro componente UserList, debemos importarlo y utilizarlo en nuestro componente principal:
import UserList from './UserList'; import StaticComponent from './StaticComponent'; function App() { return ( <div className="App"> <StaticComponent /> <UserList /> </div> ); } export default App;
De esta manera, nuestra aplicación mostrará tanto los datos estáticos (del componente StaticComponent) como los datos dinámicos obtenidos de la API (a través del componente UserList).
¿Qué más podemos mostrar de los usuarios?
La API de JSONPlaceholder proporciona varios campos para cada usuario, no solo el nombre. Podríamos expandir nuestro componente para mostrar más información, como el email, la ciudad o el nombre de la empresa:
<ul> {users.map(user => ( <li key={user.id}> <strong>{user.name}</strong> - {user.email} <p>Ciudad: {user.address.city}</p> </li> ))} </ul>
Observa cómo accedemos a propiedades anidadas como user.address.city para obtener la ciudad del usuario. Esto es posible porque los datos que recibimos de la API tienen una estructura jerárquica.
Dominar el consumo de APIs con fetch en React te permitirá crear aplicaciones más dinámicas y conectadas con servicios externos. ¿Qué otros datos te gustaría mostrar de los usuarios? ¿Quizás su teléfono o el nombre de su empresa? Comparte en los comentarios cómo implementarías estas mejoras en el componente UserList.
les quiero mostrar lo que estyo haciendo con tailwind y react
Henrry Andrés Terraza Sigcho
Mi lista quedo así con el reto.
Alejandro Esaú Maximino Olmedo
El hook más apropiado para realizar un fetch a una API cuando el componente se monta por primera vez es useEffect. Este hook permite ejecutar código en función de ciertas dependencias, y si se le pasa un array vacío como segundo argumento, se ejecuta solo una vez, al montar el componente. Esto es ideal para hacer llamadas a APIs.
Roberto Carlos Martinez Loredo
estos son los tipos de comentarios que deberian estar en el top, super importantes.
Max Andy Diaz Neyra
Arlex Fabian Urrea Torres
Platzi Team
James Yesid Herrera Acevedo
He estado tratando de realizar el reto junto con estilos de tailwind haciendo uso del modo dark y también del Grid para ordenar los datos.
Este es mi componente:
esta es la lista de usuarios (light):
Elián Yeltzin Paniagua Mariaca
Juan David Acosta González
Este es mi código, lo ajuste para hacer consumos de la API de Rick & Morty
import { useState, useEffect } from 'react'; const UsersList = () => { const [users, setUsers] = useState([]) useEffect(() => { fetch("https://rickandmortyapi.com/api/character/?page=1") .then((response) => response.json()) .then((data) => setUsers(data.results)) .catch((error) => console.error("Error fetching users:", error)); }, []) return ( <div> <h1>Users List Component</h1> <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> </div>) } export default UsersList
Alejandro Esaú Maximino Olmedo
Mi humilde aporte:
Emanuel Mendoza
Nice!
Raúl Adolfo Sánchez Rodríguez
Sofia Libertad Montoya Herrera
Jose Luis Bedoya
Alfonso Neil Jiménez Casallas
well done!
Sebastian Vásquez Valencia
Por aca practicando un poco tambien con Tailwind y agregando la ciudad
JOSE MARIA SUAREZ GIESE
En mi caso, lo que hice fue que dentro del mismo Li, añadí un salto de línea y coloqué la ciudad:
<ul> { users.map((user) => ( <li key={user.id}> Nombre: {user.name} <br /> Ciudad: {user.address.city} </li> )) } </ul>
Diego Frank Lipa Choque
si tengo una capeta que tiene dos palabras como seria
En el ecosistema de React, la convención reina es el PascalCase para componentes y el kebab-case (o a veces camelCase) para carpetas.
Si tu carpeta tiene dos palabras, como "User List", lo estándar es usar user-list.
¿Por qué? Porque en servidores y sistemas de archivos, los espacios son el enemigo; causan errores de rutas y dolores de cabeza innecesarios. El guion medio es el separador universal que evita ambigüedades.
Así que, si tu componente es UserList, tu carpeta será user-list/. Es limpio, legible y profesional.
Ahora, piensa en esto: si decides usar UserList/ (PascalCase) para la carpeta, ¿qué conflicto crees que podría surgir en sistemas operativos que no distinguen entre mayúsculas y minúsculas al intentar importar archivos?
Diego Frank Lipa Choque
Como es la nomenclature de las carpetas
Emanuel Mendoza
Para colocar la ciudad me metí al json para ver la estructura y note que city es un elemento del objeto address. Para poner la ciudad podríamos hacer algo asi:
Johan Sebastian
Aca dejo el codigo de la clase en typescript:
import { useEffect, useState } from "react"; interface User { id: string; name: string; } export const UserList = () => { const [users, setUsers] = useState<User[]>([]); useEffect(() => { fetch("https://jsonplaceholder.typicode.com/users") .then((res) => res.json()) .then((data) => setUsers(data)) .catch((err) => console.error(err.message)); }, []); return ( <div> <h2>Usuarios</h2> <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); };
import { useState, useEffect } from "react"; const UserList = () => { const [users, setUsers] = useState([]); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const fetchUsers = async () => { const url = "https://jsonplaceholder.typicode.com/users"; try { const response = await fetch(url); if (!response.ok) { throw new Error(`Error ${response.status}: error al conectar a los datos`); } const data = await response.json(); if (!Array.isArray(data)) { throw new Error("La respuesta no es una lista de usuarios"); } setUsers(data); } catch (err) { setError(err.message); setUsers([]); } finally { setLoading(false); } }; fetchUsers(); }, []); const getInitials = (name) => { if (!name) return "?"; return name .split(" ") .map((n) => n[0]) .filter(Boolean) .slice(0, 2) .join("") .toUpperCase(); }; if (loading) return <p className="text-sm text-gray-500">Cargando usuarios...</p>; if (error) return <p className="text-sm text-red-600">{error}</p>; return ( <div className="p-4"> <h2 className="text-2xl font-semibold mb-4">Lista de Usuarios</h2> <ul className="grid gap-4 sm:grid-cols-2 md:grid-cols-3"> {users.map((user) => ( <li key={user.id} className="flex items-center gap-4 p-4 bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow" > <div className="flex-none w-12 h-12 rounded-full bg-indigo-600 text-white flex items-center justify-center font-semibold text-lg"> {getInitials(user.name)} </div> <div className="flex-1 min-w-0"> <div className="flex items-center justify-between"> <h3 className="text-base font-medium truncate">{user.name || "Sin nombre"}</h3> <span className="text-sm text-gray-500">{user.address?.city || "—"}</span> </div> <p className="text-sm text-gray-600 truncate">{user.email}</p> {user.company?.name && ( <p className="text-sm text-gray-400 mt-1">{user.company.name}</p> )} </div> </li> ))} </ul> </div> ); }; export default UserList;
Para manejar el loading junto con el response y el catch en tu código de fetch, puedes seguir este ejemplo:
import React, { useState, useEffect } from 'react'; const UserList = () => { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); useEffect(() => { fetch('https://jsonplaceholder.typicode.com/users') .then(response => { if (!response.ok) throw new Error('Error en la respuesta'); return response.json(); }) .then(data => { setUsers(data); setLoading(false); }) .catch(err => { setError(err.message); setLoading(false); }); }, []); if (loading) return <div>Cargando...</div>; if (error) return <div>{error}</div>; return ( <div> <h1>Lista de usuarios</h1> <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); }; export default UserList;
Este código incluye un estado de loading que se activa al iniciar el fetch y se desactiva al recibir la respuesta o al capturar un error.
Cuando se utiliza Tailwind CSS, es posible notar que las viñetas (<ul>) y la numeración (<ol>) no se muestran por defecto. Esto ocurre debido a Preflight, una capa base de Tailwind que resetea los estilos predeterminados de los navegadores para garantizar consistencia entre ellos.
Para reactivar estos estilos, puedes usar clases específicas como list-disc (para viñetas) o list-decimal (para numeración), junto con márgenes o padding adecuados, como ml-5.
Por ejemplo, <ol className="list-decimal ml-5"> restaurará la numeración y proporcionará un espaciado apropiado.
import { useEffect, useState } from 'react'; export const Userlist = () => { const [users, setUsers] = useState([]); useEffect(() => { fetch('https://jsonplaceholder.typicode.com/users') .then(res => res.json()) .then(data => setUsers(data)) .catch(err => console.error("Ocurrio un error",err)); }, []); return ( <main> <section> <h2>Lista de Usuarios</h2> <ol className="list-decimal ml-5"> { users.map((user) =>( <li key={user.id}> { user.name + " | " + user.username}</li> )) } </ol> </section> </main> ); }
Hola!! Aqui comparto el código usando async/await para realizar el fetch
import { useState, useEffect } from "react"; const UserList = () => { const [users, setUsers] = useState([]); useEffect(() => { const fetchData = async () => { try { const response = await fetch('https://jsonplaceholder.typicode.com/users'); const data = await response.json(); setUsers(data); } catch (error) { console.error('Error fetching data', error); } }; fetchData(); }, []); // [] hace que solo se ejecute una vez (al montar el componente) return ( <div> <h2>Lista de Usuarios</h2> <ul> { users.map((user) => ( <li id={user.id}>{user.name}</li> )) } </ul> </div> ) } export default UserList;
para colocar la ciudad, observamos el modelo de datos, en este caso viene asì: <ul> {users.map((user) => ( <li key={user.id}> {user.username} - {user.email} - {user.address.city} </li> ))} </ul>
{ "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "Sincere@april.biz", "address": { "street": "Kulas Light", "suite": "Apt. 556", "city": "Gwenborough", "zipcode": "92998-3874", "geo": { "lat": "-37.3159", "lng": "81.1496" } } ```Tendriamos que acceder al objeto address y dentro de el, a city, de la siguiente forma: user.address.city y ya lo tendriamos, mas o menos asì queda la implementaciòn: ```js <ul> {users.map((user) => ( <li key={user.id}> {user.username} - {user.email} - {user.address.city} </li> ))} </ul>
import { useEffect, useState } from "react"; const UserList = () => { const [users, setUsers] = useState([]); useEffect(() => { fetch('https://jsonplaceholder.typicode.com/users') .then((response) => response.json()) .then((data) => setUsers(data)) .catch((error) => console.error('Error fetching data', error)) }, []) return ( <> <h1>Lista de Usuarios</h1> <ol> { users.map((user) => ( <li key={user.id}>{user.id} - {user.name} de la ciudad {user.address.city}</li> )) } </ol> </> ) } export default UserList;