Skip to content

Instantly share code, notes, and snippets.

@carlrip
Last active February 11, 2023 13:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carlrip/4f01187cef825f3b7d8cc953ab876b8b to your computer and use it in GitHub Desktop.
Save carlrip/4f01187cef825f3b7d8cc953ab876b8b to your computer and use it in GitHub Desktop.
Cancelling fetch in React and TypeScript
import React from "react";
type Character = {
name: string;
};
interface PromiseWithCancel<T> extends Promise<T> {
cancel: () => void;
}
function getCharacter(id: number) {
const controller = new AbortController();
const signal = controller.signal;
const promise = new Promise(async (resolve) => {
try {
const response = await fetch("https://swapi.dev/api/people/" + id, {
method: "get",
signal,
});
const data = await response.json();
assertIsCharacter(data);
resolve(data);
} catch (ex: unknown) {
if (isAbortError(ex)) {
console.log(ex.message);
}
}
});
(promise as PromiseWithCancel<Character>).cancel = () => controller.abort();
return promise as PromiseWithCancel<Character>;
}
function assertIsCharacter(data: any): asserts data is Character {
if (!("name" in data)) {
throw new Error("Not character");
}
}
function isAbortError(error: any): error is DOMException {
if (error && error.name === "AbortError") {
return true;
}
return false;
}
export function App() {
const [status, setStatus] = React.useState<
"loading" | "loaded" | "cancelled"
>("loading");
const [data, setData] = React.useState<Character | undefined>(undefined);
const [query, setQuery] = React.useState<
PromiseWithCancel<Character> | undefined
>(undefined);
React.useEffect(() => {
const q = getCharacter(1);
setQuery(q);
q.then((character) => {
setData(character);
setStatus("loaded");
});
}, []);
if (status === "loading") {
return (
<div>
<div style={{ margin: "20px 0px 5px" }}>loading ...</div>
<button
onClick={() => {
query?.cancel();
setStatus("cancelled");
}}
>
Cancel
</button>
</div>
);
}
if (status === "cancelled") {
return <div>Cancelled</div>;
}
return <div>{data && <h3>{data.name}</h3>}</div>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment