DEV Community

Cover image for Criei um hook personalizado para buscar dados da API do Rick and Morty com React Query e TypeScript
Francielle Dellamora
Francielle Dellamora

Posted on • Edited on

Criei um hook personalizado para buscar dados da API do Rick and Morty com React Query e TypeScript

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;
}


Enter fullscreen mode Exit fullscreen mode

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;


Enter fullscreen mode Exit fullscreen mode

Consumindo o hook useRickAndMortyAPI

home page of a personal project

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>
  );
}


Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
beatrizoliveira profile image
Beatriz Oliveira

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/

Collapse
 
zoldyzdk profile image
Hewerton Soares

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

Collapse
 
dellamora profile image
Francielle Dellamora

Fico feliz de verdade em saber que vai te ajudar!! Se tiver alguma dúvida pode me chamar (: