UNPKG

@ashraflabs/use-api

Version:

A simple and powerful custom React hook (useApi) that simplifies API requests with automatic loading and error states. Ideal for clean and maintainable data fetching in React components.

86 lines (85 loc) 4.51 kB
import { useCallback, useEffect, useState } from "react"; import FetchError from "./classes"; import { removeMultipleSlashes } from "./utils/removeMultipleSlashes"; /** * A custom React hook for making GET requests using the Fetch API. * @template Response The type of the response data. * @param url The API endpoint URL (relative or absolute). The request will resend when the url change. use debounce in options to make a delay * @param options Optional configuration for the GET request. * @param options.onDataGet Callback invoked with the response data on success. * @param options.onErrorResponse Callback invoked with a FetchError on failure. * @param options.debounce Delay in milliseconds before sending the request.. * @param options.preventIncludeNullOrUndefined If true, skips requests if the URL contains "undefined" or "null". * @param options.headers Custom headers for the request. * @param options.baseApiUrl Base URL to prepend to the endpoint. * @returns An object containing the response data, error, loading state, and a reload function. * @example * const { data, error, loading, reload } = useGet<User>("/api/user", { * baseApiUrl: "http://localhost:5000", * onDataGet: (data) => console.log(data), * }); */ export function useGet(baseApiUrl, url, options) { const [data, setData] = useState(); const [error, setError] = useState(); const [loading, setLoading] = useState(true); const [trigger, setTrigger] = useState(0); const reload = useCallback(() => { setTrigger((prev) => prev + 1); }, []); const cleanUrl = removeMultipleSlashes(`${baseApiUrl}/${url}`); useEffect(() => { var _a; if (!url) return; if ((options === null || options === void 0 ? void 0 : options.preventIncludeNullOrUndefined) && (cleanUrl.includes("undefined") || cleanUrl.includes("null"))) { return; } const controller = new AbortController(); const debounceDelay = (_a = options === null || options === void 0 ? void 0 : options.debounce) !== null && _a !== void 0 ? _a : 0; const timeout = setTimeout(() => { let responseCode; const fetchData = async () => { var _a, _b, _c; setLoading(true); try { const response = await fetch(cleanUrl, { method: "GET", signal: controller.signal, headers: options === null || options === void 0 ? void 0 : options.headers, }); responseCode = response.status; if (!response.ok) { throw new FetchError(`HTTP error! Status: ${response.status}`, response, response.status); } const res = await response.json(); setData(res); setError(undefined); (_a = options === null || options === void 0 ? void 0 : options.onResponse) === null || _a === void 0 ? void 0 : _a.call(options, res); } catch (err) { if (err instanceof Error && err.name !== "AbortError") { const fetchErr = err instanceof FetchError ? err : new FetchError(err.message); setError(fetchErr); setData(undefined); (_b = options === null || options === void 0 ? void 0 : options.onErrorResponse) === null || _b === void 0 ? void 0 : _b.call(options, fetchErr); if (fetchErr.status !== undefined) { responseCode = fetchErr.status || undefined; } } } finally { setLoading(false); (_c = options === null || options === void 0 ? void 0 : options.onRequestDone) === null || _c === void 0 ? void 0 : _c.call(options, cleanUrl, removeMultipleSlashes(cleanUrl.replace(baseApiUrl, "/")).split("?")[0], responseCode); } }; fetchData(); }, debounceDelay); return () => { controller.abort(); clearTimeout(timeout); }; }, [cleanUrl, trigger, options === null || options === void 0 ? void 0 : options.debounce, options === null || options === void 0 ? void 0 : options.headers]); return { data, error, loading, reload }; }