Skip to content

Commit

Permalink
add preFetchCallback to useFetch
Browse files Browse the repository at this point in the history
  • Loading branch information
konstantin-lukas committed Sep 12, 2024
1 parent 163d025 commit 30a26b6
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "anzol",
"version": "2.5.2",
"version": "2.6.0",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
Expand Down
14 changes: 12 additions & 2 deletions src/hooks/useFetch.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Dispatch, SetStateAction } from "react";
import { useEffect, useState } from "react";

/** Possible return types depending on how the result is parsed. */
Expand All @@ -22,7 +23,7 @@ export interface FetchResult<T = FetchResultData> {
status: number | undefined
}

export interface FetchOptions {
export interface FetchOptions<T> {
/** How to parse the result (determines type of returned data). Set to "response" if you
* don't want to extract the data automatically and receive the response object instead. */
parseType?: ParseType,
Expand All @@ -37,6 +38,12 @@ export interface FetchOptions {
* to re-fetch on failure. If the function returns undefined, it stops the hook from re-fetching again until an
* input parameter to the hook changes and resets the internal retry counter.*/
retryTimeout?: (attempt: number) => number | undefined,
/** This callback gets called before fetching. It gives you access to the setter function for the data and the url
* to fetch. The return value of this function is a boolean indicating whether to perform a fetch from the new
* url. The intended use for this hook is mostly to prevent fetching from URLs that you know are going
* to return a non-200 result. In that case you can keep the previous state or set some empty state depending on
* your use case. You can of course also just use this hook to execute some arbitrary code before fetching. */
preFetchCallback?: (setState: Dispatch<SetStateAction<T | undefined>>, newUrl: string) => boolean,
}
/**
* Fetches the provided URL and optionally parses the response. Aborts requests when a new request is
Expand Down Expand Up @@ -87,7 +94,8 @@ function useFetch<T = FetchResultData>(
requestOptions = {},
discardStaleRequests = true,
retryTimeout = undefined,
}: FetchOptions = {},
preFetchCallback = undefined,
}: FetchOptions<T> = {},
): FetchResult<T | undefined> {
const [status, setStatus] = useState<number>();
const [ok, setOk] = useState<boolean>(false);
Expand All @@ -100,6 +108,8 @@ function useFetch<T = FetchResultData>(
}, [url, parseType, discardStaleRequests, retryTimeout]);

useEffect(() => {
const performFetch = typeof preFetchCallback !== "function" || preFetchCallback(setData, url);
if (!performFetch) return;
const abortController = new AbortController();
const { signal } = abortController;
(async () => {
Expand Down
27 changes: 27 additions & 0 deletions tests/useFetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,4 +416,31 @@ describe("useFetch", () => {
expect(retryTimeoutNoLimit).not.toHaveBeenCalledTimes(0);
});
});

test("should allow preventing fetches via the pre fetch callback", async () => {
global.fetch = jest.fn(async () => {
await new Promise((r) => setTimeout(r, 0));
return Promise.resolve({
ok: false,
status: 404,
});
}) as jest.Mock;

const callback = jest.fn((setState) => {
setState("banana");
return false;
});

const { result } = renderHook(() => useFetch("/api", { preFetchCallback: callback }));

expect(result.current.data).toEqual("banana");
expect(result.current.loading).toBe(false);
expect(result.current.ok).toBe(false);
expect(result.current.status).toEqual(undefined);
await waitFor(() => {
expect(callback).toHaveBeenCalledTimes(1);
});
expect(global.fetch).not.toHaveBeenCalled();

});
});

0 comments on commit 30a26b6

Please sign in to comment.