DEV Community

Cover image for Criando react hook personalizado para fazer requisições
Mayderson Mello
Mayderson Mello

Posted on

Criando react hook personalizado para fazer requisições

Introdução

Este tutorial mostrará como criar um hook customizado chamado useFetcher usando o useReducer, ele vai permitir que façamos requisições HTTP de maneira fácil além de gerenciarmos o estado dessas requisições.

Imports

Vamos começar criando um arquivo chamado useFetcher.ts, gosto de colocar esses custom hooks dentro de src/hooks, agora vamos importar as dependências necessárias:

import { useCallback, useEffect, useReducer } from 'react';
Enter fullscreen mode Exit fullscreen mode

Criar hook useFetcher

Em seguida, vamos criar uma função chamada useFetcher que possui um parâmetro para a URL que desejamos fazer a requisição, essa função terá também um generic que vai auxiliar na tipagem desse hook, e o tipo StateProps representa o que será retornado desse hook:

type StateProps<T> = {
  data?: T;
  loading: boolean;
  error?: Error;
};

function useFetcher<T = unknown>(url: string): StateProps<T> {
  ...
}
Enter fullscreen mode Exit fullscreen mode

Agora vamos criar nosso estado inicial:

function useFetcher<T = unknown>(url: string): StateProps<T> {
  const initialState: StateProps<T> = {
    data: undefined,
    loading: true,
    error: undefined,
  };
}
Enter fullscreen mode Exit fullscreen mode

Certo agora precisamos criar nossa função de reducer, ela será responsável por conter nosso state e action, no state vamos ter acesso ao data, loading e error e no action teremos acesso ao type e payload:

type ActionType<T> =
  | { type: 'loading' }
  | { type: 'fetched'; payload: T }
  | { type: 'error'; payload: Error };

function useFetcher<T = unknown>(url: string): StateProps<T> {
  ...

  function reducer(state: StateProps<T>, action: ActionType<T>) {
    switch (action.type) {
      case 'loading':
        return { ...state, loading: true } as StateProps<T>;
      case 'fetched':
        return {
          ...state,
          data: action.payload,
          loading: false,
        } as StateProps<T>;
      case 'error':
        return {
          ...state,
          error: action.payload,
          loading: false,
        } as StateProps<T>;
      default:
        return state;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Basicamente, na função acima usamos o switch para verificar se o type disparado é loading, fetched ou error, e com isso retornamos um objeto, com a cópia do estado e as modificações necessárias.

Agora vamos criar nosso handleFetch, ele será responsável por realizar de fato a requisição, além disso ele realiza dispatch do type necessário em cada momento da chamada a API:

  • 1º - Ao executar a função ele dispara o type: loading.

  • 2º - Ao finalizar com sucesso a requisição ele dispara o type: fetched + payload, esse payload é os dados retornados da API.

  • 3º - Em caso de erro na requisição ele dispara o type: error + payload, esse payload será o erro que foi retornado.

function useFetcher<T = unknown>(url: string): StateProps<T> {
  ...

  const [state, dispatch] = useReducer(reducer, initialState);

  const handleFetch = useCallback(async () => {
    dispatch({ type: 'loading' });

    try {
      const response = await axios.get<T>(url);

      dispatch({ type: 'fetched', payload: response.data });
    } catch (error) {
      if (axios.isAxiosError(error) || error instanceof Error) {
        dispatch({ type: 'error', payload: error });
      }
    }
  }, [url]);

  useEffect(() => {
    handleFetch();
  }, [handleFetch]);

  return {
    data: state.data,
    loading: state.loading,
    error: state.error,
  };
}
Enter fullscreen mode Exit fullscreen mode

obs: utilizei como exemplo o axios para realizar a requisição, mas poderia ser trocado pelo fetch normalmente.

Testando useFetcher

Por fim, só falta testar o nosso custom hook:

import { useFetcher } from '../hooks/useFetcher';

interface ITodo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

const Home = () => {
  const {
    loading: loadingTodos,
    data: todosData,
    error: todosError,
  } = useFetcher<ITodo>('https://jsonplaceholder.typicode.com/todos/1');

  return (
    <div>
      {loadingTodos && <h1>Loading...</h1>}

      {todosData && <pre>{JSON.stringify(todosData, null, 2)}</pre>}

      {todosError && <h1>{todosError?.message}</h1>}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
gustavohsdp profile image
Gustavo Henrique

Muito bacana, irei testar esse custom hook para fazer minhas requisições valeu!

Collapse
 
mayderson profile image
Mayderson Mello

Opa muito obrigado, você vai curtir bastante auxilia muito no dia a dia.