这篇文章将介绍在 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...