Criando react hook personalizado para fazer requisições

mayderson

Mayderson Mello

Posted on January 27, 2023

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
💖 💪 🙅 🚩
mayderson
Mayderson Mello

Posted on January 27, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related