Lidando com requisições HTTP
Essa é uma abordagem comum, e você já deve ter visto muitos exemplos de código que fazem chamadas HTTP dentro de um componente, variando detalhes, como o uso de fetch
ou axios
, ou a forma como o estado é gerenciado.
Você já deve ter visto exemplos de como refatorar esse código para um hook customizado, mas vamos fazer isso de novo.
Esse componente é relativamente simples, você tem 3 estados dentro do componente para representar o estado da
requisição.
O useEffect é executado apenas uma vez, quando o componente é montado, e é a engrenagem principal para fazer a
requisição toda funcionar.
Primeira abordagem:
import { useEffect, useState } from "react";
function App() {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<any>(null);
const [data, setData] = useState<any>(null);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
const response = await fetch("https://fakestoreapi.com/products");
if (!response.ok) throw new Error("Failed to fetch data");
const data = await response.json();
setData(data);
setIsLoading(false);
};
fetchData().catch((e) => setError(e));
}, []);
return (
<div>
{isLoading && <p>Loading...</p>}
{error && <p>{error.message}</p>}
{data && <pre>{JSON.stringify(data)}</pre>}
</div>
);
}
export default App;
Separando a lógica de fetch em um hook
Segunda Abordagem
Nessa abordagem, isolamos a lógica de fetch em um hook customizado, que é reutilizável em qualquer componente,
mas também diminuímos a responsabilidade do componente, que agora só precisa lidar com a renderização do estado e a
requisição.
Mas isso ainda pode melhorar, o componente APP ainda precisa lidar com a lógica de fazer a requisição, apesar de não
precisar lidar com o estado da requisição.
Segunda abordagem:
// useFetch.tsx
import { useEffect, useState } from "react";
type useFetchParams<T> = {
fetcher: () => Promise<T>;
queryKey: string[];
};
export function useFetch<T>({ fetcher, queryKey }: useFetchParams<T>) {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<any>(null);
const [data, setData] = useState<T | null>(null);
const queryKeyString = JSON.stringify(queryKey);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
const data = await fetcher();
setData(data);
setIsLoading(false);
};
fetchData().catch((e) => setError(e));
}, [queryKeyString]);
return { isLoading, error, data };
}
// App.tsx
import { useFetch } from "./useFetch.tsx";
const ENDPOINT = "https://fakestoreapi.com/products";
function App() {
const { isLoading, error, data } = useFetch({
queryKey: [ENDPOINT],
fetcher: async () => {
const response = await fetch(ENDPOINT);
if (!response.ok) throw new Error("Failed to fetch data");
return response.json();
},
})
return (
<div>
{isLoading && <p>Loading...</p>}
{error && <p>{error.message}</p>}
{data && <pre>{JSON.stringify(data)}</pre>}
</div>
);
}
export default App;
Separando a Requisição em um Hook Customizado
Agora, é o que eu considero ideal como separação de responsabilidades.
- O Hook customizado
useFetch
é responsável por lidar com a lógica de fazer a requisição; - O hook useProductFindAll é responsável por fazer a requisição de produtos;
- O componente App é responsável por lidar com a renderização dos produtos e o estado da requisição.
Terceira Abordagem
// useProductFindAll.tsx
import { useFetch } from "./useFetch.tsx";
const ENDPOINT = "https://fakestoreapi.com/products";
async function fetchProductFindAll(params = {}) {
const searchParams = new URLSearchParams(params);
const response = await fetch(ENDPOINT + `?${searchParams.toString()}`);
if (!response.ok) throw new Error("Failed to fetch data");
return response.json();
}
export function useProductFindAll(params = {}) {
return useFetch({
queryKey: [ENDPOINT, params],
fetcher: () => fetchProductFindAll(params)
});
}
// App.tsx
import { useProductFindAll } from "./useProductFindAll.tsx";
function App() {
const { isLoading, error, data } = useProductFindAll({ limit: 6 })
return (
<div>
{isLoading && <p>Loading...</p>}
{error && <p>{error.message}</p>}
{data && <pre>{JSON.stringify(data)}</pre>}
</div>
);
}
export default App;
Se você tem experiência ou conhece a biblioteca react-query
, você pode perceber que o hook useFetch
é muito
parecido
com o hook useQuery
do react-query
, e é verdade.
O react-query
é uma biblioteca que abstrai a lógica de fazer requisições, e é uma ótima alternativa para lidar com
estado "back-end" no front-end. A biblioteca é muito poderosa e tem muitos recursos, como cache, refetch, paginação.
Por isso, se você está lidando com muitas requisições no seu projeto, eu recomendo que você dê uma olhada na biblioteca
react-query
.
Top comments (0)