⚛️ Consumir API en React
En este artículo, aprenderemos a consumir una API usando React especificamente la API de Rick and Morty. Esta API es excelente para practicar, ya que es una API púiblica que devuelve datos de personajes, episodios y ubicaciones de la serie animada.
Crear el Proyecto con Vite
Para crear un nuevo proyecto de React y configurado con TypeScript utilizando Vite, puedes usar el siguiente comando:
1
npm create vite@latest rick-and-morty-api -- --template react-ts
Una vez creado el proyecto, navegamos a la carpeta generada y ejecutamos el comando para instalar las dependencias:
1
2
cd rick-and-morty-api
npm install
Luego de forma opcional, inicia el servidor de desarrollo para ver la aplicación que nos crea vite:
1
npm run dev
Estructura de Archivos
A pesar de que vite nos crea la estructura y la configuración, recomiendo organizar los archivos de la carpeta src
de la siguiente manera:
1
2
3
4
5
6
7
8
9
10
src/
├── assets/
│ └── styles.css # Estilos globales para los temas
├── components/
│ └── ToggleTheme.tsx # Componente que cambia el tema
├── context/
│ └── ThemeContext.tsx # Contexto para gestionar el tema
├── App.tsx # Componente principal de la aplicación
├── index.tsx # Punto de entrada de la aplicación
└── index.css # Estilos globales para la aplicación
Creación un Hook personalizado para la API
Ahora vamos a crear un hook personalizado llamado useRickAndMortyAPI
que nos permita consumir la API de Rick and Morty y gestionar el estado de carga y error.
Definir la Estructura de los Datos
Primero, vamos a definir las interfaces para los datos que vamos a recibir de la API. La API de Rick and Morty devuelve una lista de personajes y su información en un formato específico, así que vamos a crear una interfaz para representar esta estructura de datos.
Creamos una nueva carpeta llamada hooks
dentro de src/
y dentro de ella, creamos el archivo useRickAndMortyAPI.ts
y agregamos lo siguiente:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import { useState, useEffect } from 'react';
import axios from 'axios';
interface Character {
id: number;
name: string;
image: string;
species: string;
}
interface ApiResponse {
results: Character[];
info: {
next: string | null;
};
}
const useRickAndMortyAPI = (url: string) => {
const [characters, setCharacters] = useState<Character[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchCharacters = async () => {
setLoading(true);
try {
const response = await axios.get<ApiResponse>(url);
setCharacters(response.data.results);
setLoading(false);
} catch (err) {
setError('Error fetching data');
setLoading(false);
}
};
fetchCharacters();
}, [url]);
return { characters, loading, error };
};
export default useRickAndMortyAPI;
Resumiendo el código anterior, tenemos lo siguiente:
- Estados:
characters
: Almacena los personajes obtenidos de la API.loading
: Indica si la petición aún esta en curso.error
: Almacena cualquier mensaje de error si la solicitud falla.
useEffect
: Se usa para ejecutar la funciónfetchCharacters
cuando el componente se monta o cual la URL cambia (en este caso, cuando cambiamos la página de personajes).axios.get
: Se usa para realizar solicitudes HTTP a la API. El tipo de respuesta esperado esApiresponse
, que contiene una lista de personajes.
Crear el Componente para Mostrar los Personajes
Ahora vamos a crear un componente llamado CharacterList
que use el hook useRickAndMortyAPI
para obtener y mostrar los personajes.
Creamos el archivo CharacterList.tsx
dentro de src/componentes
y aquí está el código para el componente CharacterList
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import React, { useState } from 'react';
import useRickAndMortyAPI from '../hooks/useRickAndMortyAPI';
const CharacterList: React.FC = () => {
const [page, setPage] = useState(1);
const { characters, loading, error } = useRickAndMortyAPI(
`https://rickandmortyapi.com/api/character?page=${page}`
);
const handleNextPage = () => {
setPage((prevPage) => prevPage + 1);
};
const handlePrevPage = () => {
if (page > 1) setPage((prevPage) => prevPage - 1);
};
if (loading) return <div>Loading...</div>;
if (error) return <div>{error}</div>;
return (
<div>
<h1>Rick and Morty Characters</h1>
<div className="pagination">
<button onClick={handlePrevPage} disabled={page <= 1}>
Anterior
</button>
<button onClick={handleNextPage}>
Siguiente
</button>
</div>
<div className="character-list">
{characters.map((character) => (
<div key={character.id} className="character-card">
<img src={character.image} alt={character.name} />
<h3>{character.name}</h3>
<p>{character.species}</p>
</div>
))}
</div>
</div>
);
};
export default CharacterList;
Resumiendo el código anterior, tenemos lo siguiente:
URL Dependiente del Estado page
- La URL que pasamos al hook
useRickAndMortyAPI
ahora incluye la variablepage
, lo que hace que la URL cambie cada vez que el valor depage
cambie. - Como
page
está controlado conuseState
y eluseEffect
en el hook depende de la URL, el hook se ejecutará cada vez que el estadopage
cambie.
Actualización de la Páginación:
- Cuando haces clic en el botón Anterior o Siguiente, decrementamos o incrementamos el valor de
page
, lo que también cambia la URl y hace que el hook se ejecute de nuevo.
Agregar Estilos Básicos
Para mejorar la apariencia de nuestra aplicación, podemos agregar algunos estilos. Vamos a agregar o remplazar los estilos en src/App.css
con los siguientes estilos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
.character-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
margin-top: 20px;
}
.character-card {
background: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
overflow: hidden;
width: 150px;
text-align: center;
}
.character-card img {
width: 100%;
height: 150px;
object-fit: cover;
}
.character-card h3 {
font-size: 16px;
margin: 10px 0;
}
.character-card p {
font-size: 14px;
color: gray;
}
.pagination {
text-align: center;
margin-top: 20px;
}
.pagination button {
background-color: #2c3e50;
color: white;
border: none;
padding: 10px;
cursor: pointer;
margin: 5px;
border-radius: 5px;
}
.pagination button:disabled {
background-color: #ccc;
}
Importar el Componente en App.tsx
Finalmente, integramos todo en el componente App.tsx
para mostrar la lista de personajes:
1
2
3
4
5
6
7
import CharacterList from "./components/CharacterList";
function App() {
return <CharacterList />
}
export default App;
Mi lema es