Você sabe o que é React Query?
De forma resumida, o React Query é uma biblioteca JavaScript open source que foi desenvolvida com o objetivo de facilitar o gerenciamento de dados em aplicações React. Ele foi criado por Tanner Linsley em 2020 e rapidamente ganhou popularidade entre desenvolvedores devido à sua abordagem inovadora e eficiente para buscar e armazenar dados.
A grande sacada do React Query é que ele permite criar aplicações React mais rápidas e escaláveis, com recursos legais como cache inteligente, paginação, cancelamento de requisições, atualizações em tempo real e muito mais. Com isso, a gente acaba simplificando o código, tornando ele mais fácil de entender e mantê-lo. E o melhor de tudo? A gente economiza tempo, porque não precisa escrever tanta lógica de busca e armazenamento de dados do zero.
Interfaces
Para otimizar a criação das interfaces, primeiramente consultei a documentação da The Rick and Morty API para identificar quais dados eram fornecidos. Uma vez encontrados, utilizei o primeiro gerador de JSON disponível na pesquisa do Google para gerar um JSON com base nesses dados. Por fim, utilizei a extensão Paste JSON as Code para gerar automaticamente as interfaces necessárias.
export interface BaseSearchResponse<T> {
info: Info;
results: T[];
}
export interface Info {
count: number;
pages: number;
next?: string
prev?: string
}
export interface Character {
id: number;
name: string;
status: Status;
species: Species;
type: string;
gender: Gender;
origin: LocationCharacter;
location: LocationCharacter;
image: string;
episode: string[];
url: string;
created: string;
}
export enum Gender {
Female = "Female",
Male = "Male",
Unknown = "unknown",
}
export interface LocationCharacter {
name: string;
url: string;
}
export enum Species {
Alien = "Alien",
Human = "Human",
}
export enum Status {
Alive = "Alive",
Dead = "Dead",
Unknown = "unknown",
}
export interface Location {
id: number;
name: string;
type: string;
dimension: string;
residents: string[];
url: string;
created: string;
}
export interface Episode {
id: number;
name: string;
air_date: string;
episode: string;
characters: string[];
url: string;
created: string;
}
Vale destacar que a interface BaseSearchResponse<T>
é um modelo de tipo genérico que representa uma resposta de uma pesquisa na API.
O tipo genérico T
é usado para especificar o tipo de dado que a resposta irá conter. Por exemplo, se a pesquisa for sobre personagens, então T
seria a interface Character
.
Passo a passo de como criei o hook personalizado
Para começar, eu defini um type Endpoints
que associa cada tipo de dado que eu quero buscar na API (location
, character
, episode
) a sua respectiva interface.
Depois disso, criei um hook personalizado chamado useRickAndMortyAPI
, que pode ser usado para fazer chamadas à The Rick and Morty API. Ele recebe um parâmetro endpoint, que é uma string que representa o tipo de recurso que você quer buscar.
Para fazer as chamadas à API, eu criei uma função chamada getAPI
usando o hook useCallback
. Essa função recebe um objeto com um parâmetro opcional pageParam
que representa o número da página que você quer buscar. Ela então usa o fetch
para fazer a chamada à API e retorna os resultados em um objeto com duas propriedades: data
(os dados retornados pela API) e nextCursor
(o número da próxima página de resultados que deve ser buscada).
Em seguida, eu uso o hook useInfiniteQuery
do React Query para buscar os dados da API de forma infinita. Ele usa a função getAPI
como queryFn
e também inclui algumas opções como keepPreviousData
para manter os dados anteriores e getNextPageParam
para obter o número da próxima página de resultados.
Finalmente, o hook useRickAndMortyAPI
retorna um objeto com as propriedades isLoading
(se os dados estão sendo carregados), data
(os dados retornados pela API) e fetchNextPage
(uma função que pode ser usada para buscar a próxima página de resultados).
/* eslint-disable @typescript-eslint/no-unused-vars */
import { useCallback } from "react";
import type { Location, Character, Episode } from "../../domain/interfaces";
import { useInfiniteQuery } from "@tanstack/react-query";
type Endpoints = {
location: Location;
character: Character;
episode: Episode;
};
const useRickAndMortyAPI = <T extends keyof Endpoints>(endpoint: T) => {
const getAPI = useCallback(
async ({ pageParam = 1 }) => {
const APILInk = "https://rickandmortyapi.com/api/";
const response =
(
await fetch(`${APILInk}/${endpoint}?page=${pageParam}`).then(res =>
res.json(),
)
)?.results || ([] as Endpoints[T][]);
return {
data: response,
nextCursor: pageParam + 1,
};
},
[endpoint],
);
const { isLoading, data, fetchNextPage } = useInfiniteQuery({
queryKey: [`apiCall-${endpoint}`],
queryFn: getAPI,
keepPreviousData: true,
getNextPageParam: lastPage => lastPage.nextCursor,
});
return {
isLoading,
data: !data
? []
: data.pages.reduce((acc, curr) => {
return [...acc, ...curr.data];
}, [] as Endpoints[T][]),
fetchNextPage,
};
};
export default useRickAndMortyAPI;
Consumindo o hook useRickAndMortyAPI
Para desenvolver a tela da imagem acima, comecei importando o hook useRickAndMortyAPI
na página Home e passei o endpoint character
como argumento.
Em seguida, desestruturei os dados que recebi do hook e armazenei a lista de personagens na variável characters
, a flag booleana isLoading
e a função fetchNextPage
.
Para renderizar os dados, passei a lista de personagens como propriedade para o componente Grid. Também adicionei uma função onLoadMore
que é chamada quando o usuário clica no botão "Load more". Essa função verifica se a flag isLoading
é verdadeira antes de chamar a função fetchNextPage
para buscar mais dados.
Dessa forma, consegui consumir os dados da API Rick and Morty na página Home e exibi-los em uma interface de usuário amigável e responsiva, permitindo que o usuário carregue mais dados à medida que navega.
import Grid from "../modules/hero/grid";
import useRickAndMortyAPI from "../common/hooks/useRickAndMortyAPI";
export default function Home() {
const {
data: characters,
isLoading,
fetchNextPage,
} = useRickAndMortyAPI("character");
return (
<div>
<Grid
rickandmortyAPI={characters}
onLoadMore={() => {
if (isLoading) return;
fetchNextPage();
}}
/>
</div>
);
}
Conclusão
Desenvolver esse projeto foi uma experiência valiosa para mim, pois me permitiu aprender mais sobre o React Query e a aplicação prática dos generics do Typescript.
Com base nesse conhecimento, eu criei um hook que possibilita a busca de dados da API do Rick and Morty de forma paginada e genérica, tornando mais fácil e flexível a utilização desses dados em diferentes partes do meu projeto.
Sugestões de melhorias e comentários são bem-vindos e apreciados!
Top comments (3)
Parabéns pelo post, a didática ficou ótima!! Foi muito legal ler cada step do processo e como o TypeScript em conjunto do React Query deixou simples e fácil a compreensão \o/
Muito bom o seu post! Bem simples e compreensivo, vai me ajudar bastante já que também estou vendo React Query, TS e consumo de API
Fico feliz de verdade em saber que vai te ajudar!! Se tiver alguma dúvida pode me chamar (: