Criei um hook personalizado para buscar dados da API do Rick and Morty com React Query e TypeScript
Tags: typescript, react, programming, braziliandevs
Published: 2023-02-23 03:35:21

###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](https://rickandmortyapi.com/documentation) 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](https://marketplace.visualstudio.com/items?itemName=quicktype.quicktype) para gerar automaticamente as interfaces necessárias.

```TypeScript
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). 





```typescript
/* 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


![home page of a personal project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b16fs2gyh4p1u1kwuooc.png)

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.


```typescript
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!