mirror of
https://github.com/prymitive/karma
synced 2026-08-28 11:17:28 +00:00
fix(ui): use atomic update in useFetchGet
This commit is contained in:
committed by
Łukasz Mierzwa
parent
a38c1fc4a1
commit
a24b71166b
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
import { renderHook } from "@testing-library/react-hooks";
|
||||
import { renderHook, act } from "@testing-library/react-hooks";
|
||||
|
||||
import { mount } from "enzyme";
|
||||
|
||||
@@ -31,7 +31,7 @@ describe("useFetchGet", () => {
|
||||
|
||||
it("sends a GET request", async () => {
|
||||
const { waitForNextUpdate } = renderHook(() =>
|
||||
useFetchGet("http://localhost/ok")
|
||||
useFetchGet<string>("http://localhost/ok")
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
@@ -45,7 +45,7 @@ describe("useFetchGet", () => {
|
||||
|
||||
it("sends correct headers", async () => {
|
||||
const { waitForNextUpdate } = renderHook(() =>
|
||||
useFetchGet("http://localhost/ok")
|
||||
useFetchGet<string>("http://localhost/ok")
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
@@ -61,12 +61,14 @@ describe("useFetchGet", () => {
|
||||
|
||||
it("doesn't send any request if autorun=false", async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useFetchGet("http://localhost/ok", { autorun: false })
|
||||
useFetchGet<string>("http://localhost/ok", { autorun: false })
|
||||
);
|
||||
|
||||
expect(fetchMock.calls()).toHaveLength(0);
|
||||
|
||||
result.current.get();
|
||||
act(() => {
|
||||
result.current.get();
|
||||
});
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(fetchMock.calls()).toHaveLength(1);
|
||||
@@ -75,7 +77,7 @@ describe("useFetchGet", () => {
|
||||
|
||||
it("will retry failed requests", async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useFetchGet("http://localhost/error")
|
||||
useFetchGet<string>("http://localhost/error")
|
||||
);
|
||||
|
||||
// initial state
|
||||
@@ -129,7 +131,7 @@ describe("useFetchGet", () => {
|
||||
|
||||
it("response is updated after successful fetch", async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useFetchGet("http://localhost/ok")
|
||||
useFetchGet<string>("http://localhost/ok")
|
||||
);
|
||||
|
||||
expect(result.current.response).toBe(null);
|
||||
@@ -153,7 +155,7 @@ describe("useFetchGet", () => {
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useFetchGet("http://localhost/500/json")
|
||||
useFetchGet<string>("http://localhost/500/json")
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
@@ -171,7 +173,7 @@ describe("useFetchGet", () => {
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useFetchGet("http://localhost/500/text")
|
||||
useFetchGet<string>("http://localhost/500/text")
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
@@ -184,7 +186,7 @@ describe("useFetchGet", () => {
|
||||
|
||||
it("error is updated after failed fetch", async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useFetchGet("http://localhost/error")
|
||||
useFetchGet<string>("http://localhost/error")
|
||||
);
|
||||
|
||||
expect(result.current.response).toBe(null);
|
||||
@@ -210,7 +212,7 @@ describe("useFetchGet", () => {
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useFetchGet("http://localhost/json/invalid")
|
||||
useFetchGet<string>("http://localhost/json/invalid")
|
||||
);
|
||||
|
||||
expect(result.current.response).toBe(null);
|
||||
@@ -236,7 +238,7 @@ describe("useFetchGet", () => {
|
||||
});
|
||||
|
||||
const Component = () => {
|
||||
const { response, error, isLoading } = useFetchGet(
|
||||
const { response, error, isLoading } = useFetchGet<string>(
|
||||
"http://localhost/slow/ok"
|
||||
);
|
||||
return (
|
||||
@@ -265,7 +267,7 @@ describe("useFetchGet", () => {
|
||||
});
|
||||
|
||||
const Component = () => {
|
||||
const { response, error, isLoading } = useFetchGet(
|
||||
const { response, error, isLoading } = useFetchGet<string>(
|
||||
"http://localhost/slow/500"
|
||||
);
|
||||
return (
|
||||
@@ -293,7 +295,7 @@ describe("useFetchGet", () => {
|
||||
});
|
||||
|
||||
const Component = () => {
|
||||
const { response, error, isLoading } = useFetchGet(
|
||||
const { response, error, isLoading } = useFetchGet<string>(
|
||||
"http://localhost/slow/error"
|
||||
);
|
||||
return (
|
||||
@@ -322,7 +324,7 @@ describe("useFetchGet", () => {
|
||||
});
|
||||
|
||||
const Component = () => {
|
||||
const { response, error, isLoading } = useFetchGet(
|
||||
const { response, error, isLoading } = useFetchGet<string>(
|
||||
"http://localhost/slow/json/invalid"
|
||||
);
|
||||
return (
|
||||
@@ -348,7 +350,7 @@ describe("useFetchGet", () => {
|
||||
});
|
||||
|
||||
const Component = () => {
|
||||
const { response, error, isLoading } = useFetchGet(
|
||||
const { response, error, isLoading } = useFetchGet<string>(
|
||||
"http://localhost/slow/text"
|
||||
);
|
||||
return (
|
||||
@@ -385,11 +387,10 @@ describe("useFetchGet", () => {
|
||||
jest.useRealTimers();
|
||||
|
||||
const Component = () => {
|
||||
const {
|
||||
response,
|
||||
error,
|
||||
isLoading,
|
||||
} = useFetchGet("http://localhost/slow/body", { fetcher: fetcher });
|
||||
const { response, error, isLoading } = useFetchGet<string>(
|
||||
"http://localhost/slow/body",
|
||||
{ fetcher: fetcher }
|
||||
);
|
||||
return (
|
||||
<span>
|
||||
<span>{response}</span>
|
||||
|
||||
+50
-20
@@ -14,6 +14,14 @@ export interface FetchGetOptionsT {
|
||||
fetcher?: null | FetchFunctionT;
|
||||
}
|
||||
|
||||
interface ResponseState<T> {
|
||||
response: null | T;
|
||||
error: null | string;
|
||||
isLoading: boolean;
|
||||
isRetrying: boolean;
|
||||
retryCount: number;
|
||||
}
|
||||
|
||||
const useFetchGet = <T>(
|
||||
uri: string,
|
||||
{ autorun = true, deps = [], fetcher = null }: FetchGetOptionsT = {}
|
||||
@@ -26,11 +34,13 @@ const useFetchGet = <T>(
|
||||
get: () => void;
|
||||
cancelGet: () => void;
|
||||
} => {
|
||||
const [response, setResponse] = useState<T | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [isRetrying, setIsRetrying] = useState<boolean>(false);
|
||||
const [retryCount, setRetryCount] = useState<number>(0);
|
||||
const [response, setResponse] = useState<ResponseState<T>>({
|
||||
response: null,
|
||||
error: null,
|
||||
isLoading: autorun,
|
||||
isRetrying: false,
|
||||
retryCount: 0,
|
||||
});
|
||||
const isCanceled = useRef<boolean>(false);
|
||||
|
||||
const cancelGet = useCallback(() => {
|
||||
@@ -41,11 +51,15 @@ const useFetchGet = <T>(
|
||||
isCanceled.current = false;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setRetryCount(0);
|
||||
setError(null);
|
||||
setResponse((r) => ({
|
||||
...r,
|
||||
isLoading: true,
|
||||
isRetrying: false,
|
||||
retryCount: 0,
|
||||
}));
|
||||
|
||||
const res = await promiseRetry(
|
||||
(retry: (err: Error) => Promise<Response>, number: number) =>
|
||||
(retry: (err: Error) => Promise<Response>, n: number) =>
|
||||
(fetcher || fetch)(
|
||||
uri,
|
||||
merge(
|
||||
@@ -55,13 +69,16 @@ const useFetchGet = <T>(
|
||||
},
|
||||
CommonOptions,
|
||||
{
|
||||
mode: number <= FetchRetryConfig.retries ? "cors" : "no-cors",
|
||||
mode: n <= FetchRetryConfig.retries ? "cors" : "no-cors",
|
||||
}
|
||||
) as RequestInit
|
||||
).catch((err: Error) => {
|
||||
if (!isCanceled.current) {
|
||||
setIsRetrying(true);
|
||||
setRetryCount(number);
|
||||
setResponse((r) => ({
|
||||
...r,
|
||||
isRetrying: true,
|
||||
retryCount: n,
|
||||
}));
|
||||
return retry(err);
|
||||
}
|
||||
}),
|
||||
@@ -79,18 +96,31 @@ const useFetchGet = <T>(
|
||||
|
||||
if (!isCanceled.current) {
|
||||
if (res.ok) {
|
||||
setResponse(body);
|
||||
setResponse({
|
||||
response: body,
|
||||
error: null,
|
||||
isLoading: false,
|
||||
isRetrying: false,
|
||||
retryCount: 0,
|
||||
});
|
||||
} else {
|
||||
setError(body);
|
||||
setResponse({
|
||||
response: null,
|
||||
error: body,
|
||||
isLoading: false,
|
||||
isRetrying: false,
|
||||
retryCount: 0,
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
setIsRetrying(false);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
setError(error.message);
|
||||
setIsLoading(false);
|
||||
setIsRetrying(false);
|
||||
setResponse((r) => ({
|
||||
...r,
|
||||
error: error.message,
|
||||
isLoading: false,
|
||||
isRetrying: false,
|
||||
}));
|
||||
}
|
||||
}, [uri, fetcher]);
|
||||
|
||||
@@ -100,7 +130,7 @@ const useFetchGet = <T>(
|
||||
return () => cancelGet();
|
||||
}, [uri, get, cancelGet, autorun, ...deps]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return { response, error, isLoading, isRetrying, retryCount, get, cancelGet };
|
||||
return { get, cancelGet, ...response };
|
||||
};
|
||||
|
||||
export { useFetchGet };
|
||||
|
||||
Reference in New Issue
Block a user