fix(ui): check if request was canceled on slow body read

This commit is contained in:
Łukasz Mierzwa
2020-06-27 12:02:47 +01:00
committed by Łukasz Mierzwa
parent 09972e6c57
commit 506177884d
4 changed files with 101 additions and 29 deletions
+19 -17
View File
@@ -4,7 +4,7 @@ import merge from "lodash.merge";
import { CommonOptions } from "Common/Fetch";
const useFetchAny = (upstreams) => {
const useFetchAny = (upstreams, { fetcher = null } = {}) => {
const [index, setIndex] = useState(0);
const [response, setResponse] = useState({
response: null,
@@ -37,7 +37,7 @@ const useFetchAny = (upstreams) => {
inProgress: true,
});
try {
const res = await fetch(
const res = await (fetcher || fetch)(
uri,
merge({}, { method: "GET" }, CommonOptions, options)
);
@@ -51,23 +51,25 @@ const useFetchAny = (upstreams) => {
body = await res.text();
}
if (res.ok) {
setResponse({
response: body,
error: null,
responseURI: uri,
inProgress: false,
});
} else {
if (upstreams.length > index + 1) {
setIndex(index + 1);
} else {
if (!isCancelled) {
if (res.ok) {
setResponse({
response: null,
error: body,
responseURI: null,
response: body,
error: null,
responseURI: uri,
inProgress: false,
});
} else {
if (upstreams.length > index + 1) {
setIndex(index + 1);
} else {
setResponse({
response: null,
error: body,
responseURI: null,
inProgress: false,
});
}
}
}
}
@@ -96,7 +98,7 @@ const useFetchAny = (upstreams) => {
return () => {
isCancelled = true;
};
}, [upstreams, index, reset]);
}, [upstreams, index, reset, fetcher]);
return { ...response, reset };
};
+31
View File
@@ -323,4 +323,35 @@ describe("useFetchAny", () => {
expect(result.current.inProgress).toBe(false);
expect(result.current.responseURI).toBe(null);
});
it("doesn't update response after cleanup on slow body read", async () => {
let tree;
const fetcher = jest.fn(() =>
Promise.resolve({
headers: {
get: () => "text/plain",
},
text: async () => {
tree.unmount();
return "ok";
},
})
);
const upstreams = [{ uri: "http://localhost/slow/body", options: {} }];
const Component = () => {
const { response, error, inProgress } = useFetchAny(upstreams, {
fetcher: fetcher,
});
return (
<span>
<span>{response}</span>
<span>{error}</span>
<span>{inProgress}</span>
</span>
);
};
tree = mount(<Component />);
});
});
+14 -9
View File
@@ -6,7 +6,10 @@ import promiseRetry from "promise-retry";
import { CommonOptions, FetchRetryConfig } from "Common/Fetch";
const useFetchGet = (uri, { autorun = true, deps = [] } = {}) => {
const useFetchGet = (
uri,
{ autorun = true, deps = [], fetcher = null } = {}
) => {
const [response, setResponse] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(true);
@@ -27,7 +30,7 @@ const useFetchGet = (uri, { autorun = true, deps = [] } = {}) => {
setError(null);
const res = await promiseRetry(
(retry, number) =>
fetch(
(fetcher || fetch)(
uri,
merge(
{},
@@ -58,20 +61,22 @@ const useFetchGet = (uri, { autorun = true, deps = [] } = {}) => {
body = await res.text();
}
if (res.ok) {
setResponse(body);
} else {
setError(body);
if (!isCanceled.current) {
if (res.ok) {
setResponse(body);
} else {
setError(body);
}
setIsLoading(false);
setIsRetrying(false);
}
setIsLoading(false);
setIsRetrying(false);
}
} catch (error) {
setError(error.message);
setIsLoading(false);
setIsRetrying(false);
}
}, [uri]);
}, [uri, fetcher]);
useEffect(() => {
if (autorun) get();
+37 -3
View File
@@ -11,8 +11,6 @@ import { useFetchGet } from "./useFetchGet";
describe("useFetchGet", () => {
beforeAll(() => {
jest.useFakeTimers();
fetchMock.mock("http://localhost/ok", {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ status: "ok" }),
@@ -23,6 +21,7 @@ describe("useFetchGet", () => {
});
beforeEach(() => {
jest.useFakeTimers();
fetchMock.resetHistory();
});
@@ -116,7 +115,7 @@ describe("useFetchGet", () => {
expect(result.current.retryCount).toBe(FetchRetryConfig.retries + 1);
expect(fetchMock.calls()).toHaveLength(FetchRetryConfig.retries + 1);
expect(fetchMock.lastCall()[0]).toBe("http://localhost/error");
expect(fetchMock.lastUrl()).toBe("http://localhost/error");
//verify headers for each request
for (let i = 0; i <= FetchRetryConfig.retries; i++) {
@@ -367,4 +366,39 @@ describe("useFetchGet", () => {
jest.runOnlyPendingTimers();
await fetchMock.flush(true);
});
it("doesn't update response after cleanup on slow body read", async () => {
FetchRetryConfig.retries = 0;
let tree;
const fetcher = jest.fn(() =>
Promise.resolve({
headers: {
get: () => "text/plain",
},
text: async () => {
tree.unmount();
return "ok";
},
})
);
jest.useRealTimers();
const Component = () => {
const {
response,
error,
isLoading,
} = useFetchGet("http://localhost/slow/body", { fetcher: fetcher });
return (
<span>
<span>{response}</span>
<span>{error}</span>
<span>{isLoading}</span>
</span>
);
};
tree = mount(<Component />);
});
});