这篇文章将介绍在 React 和 TypeScript 利用中, 如何勾销 fetch 申请
一个 React 组件
以下是一个通过 Web API 获取一些数据并进行渲染的 React 组件
export function App() {const [status, setStatus] = React.useState<
"loading" | "loaded" | "cancelled"
>("loading");
const [data, setData] = React.useState<Character | undefined>(undefined);
React.useEffect(() => {getCharacter(1).then((character) => {setData(character);
setStatus("loaded");
});
}, []);
if (status === "loading") {
return (
<div>
<div>loading ...</div>
<button>Cancel</button>
</div>
);
}
if (status === "cancelled") {return <div>Cancelled</div>;}
return <div>{data && <h3>{data.name}</h3>}</div>;
}
网络数据通过 useEffect
中的 getCharacter
函数获取,而后再应用 useState
设置到 data
中
同时,咱们定义一个status
变量用来存储申请数据时的状态(‘ 申请中 ’/’ 申请已实现 ’/’ 申请已勾销 ’/). 留神,当状态处于 ’ 申请中 ’ 时,界面会渲染一个”勾销“按钮
当点击”勾销“按钮后,咱们就能够勾销 fetch 申请
接下来,让咱们看看 getCharacter
函数外面有什么
async function getCharacter(id: number) {const response = await fetch(`https://swapi.dev/api/people/${id}/`);
const data = await response.json();
assertIsCharacter(data);
return data;
}
代码中通过 fetch 调用了一个收费的接口并返回申请到的数据
上面 Character
的类型
type Character = {name: string;};
接下来,再看看 assertIsCharacter
函数
function assertIsCharacter(data: any): asserts data is Character {if (!("name" in data)) {throw new Error("Not character");
}
}
通过 TypeScript 的 类型断言 将数据类型设置为Character
。
应用 AbortController
来勾销 fetch
1AbortController 是 JavaScript 的最新版本中的个性,它是在 fetch
被实现之后呈现的。更好的音讯是所有古代浏览器都反对它。
AbortController
蕴含一个 abort
办法。它还蕴含一个能够传递给 fetch
的signal
属性。当调用 AbortController.abort
时,fetch
申请就会被勾销。
让咱们在 getCharacter
的 fetch
申请中应用 AbortController
及其 signal
属性:
function getCharacter(id: number) {
// 获取 AbortController 实例
const controller = new AbortController();
// 获取 signal 属性
const signal = controller.signal;
const promise = new Promise(async (resolve) => {const response = await fetch(`https://swapi.dev/api/people/${id}/`, {
method: "get",
// 将 signal 作为 fetch 的参数之一
signal,
});
const data = await response.json();
assertIsCharacter(data);
resolve(data);
});
// 设置一个 勾销函数
promise.cancel = () => controller.abort();
return promise;
}
咱们从 getCharacter
函数中删除了 async
关键字,并将现有代码包装在一个新的 Promise
中。当申请到数据后,咱们应用 resolve
将数据抛出去。咱们在 Promise
中增加了一个 cancel
办法,该办法调用了AbortController.abort
。
蕴含 cancel
办法的 Promise
从 getCharacter
中被返回,以便咱们在业务代码中能够应用它来勾销申请。
保留代码查看界面成果,发现有一个类型谬误:
// 💥 - Property 'cancel' does not exist on type 'Promise<unknown>'
promise.cancel = () => controller.abort();
让咱们为 Promise
的 cancel
办法创立一个类型
interface PromiseWithCancel<T> extends Promise<T> {cancel: () => void;
}
// 应用 然应用类型断言来解决后面的类型谬误
function getCharacter(id: number) {
...
(promise as PromiseWithCancel<Character>).cancel = () => controller.abort();
return promise as PromiseWithCancel<Character>;
}
在 React 组件中应用getCharacter
咱们将把 getCharacter
返回的 promise 存储在一个名为 query
的状态变量中。
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");
});
}, []);
...
当初,当点击勾销按钮的时候,咱们调用 promise 中的 cancle
办法
<button
onClick={() => {query?.cancel();
setStatus("cancelled");
}}
>
Cancel
</button>
点击勾销按钮后吗,咱们看到发现’Cancelled‘文案被渲染进去了
再通过谷歌开发者工具查看网络申请,发现申请也被勾销了
很棒是吧!😊
捕捉 ” 勾销申请 ” 产生的谬误
让咱们再看看控制台,当申请被勾销后,有谬误被抛出了
咱们能够应用 try catch
来包裹申请以便捕捉谬误
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);
}
}
});
isAbortError
类型的函数如下
function isAbortError(error: any): error is DOMException {if (error && error.name === "AbortError") {return true;}
return false;
}
当初当咱们再次点击勾销按钮,咱们会在控制台收到一条音讯提醒而不是一个谬误
总结
能够将 AbortController
中的 signal
属性传递给 fetch
。而后能够调用AbortController.abort
勾销申请。
勾销 fetch 会引发一个谬误,但能够应用 try catch
将其捕捉。
参考文档:https://www.carlrippon.com/ca…