背景
在使用useEffect中用啦回調函數中使用 async...await... 這時候就會報錯。
上面代碼可以看到,在報錯,effect function 應該返回一個銷毀函數(effect:是指return返回的cleanup函數),如果 useEffect 第一個參數傳入 async,返回值則變成了 Promise,結果就是會導致 react 在調用銷毀函數的時候報錯。
React 為什么要這么做?
useEffect 作為 Hooks 中一個很重要的 Hooks,它能夠讓你在函數組件中執行副作用操作。 也能夠完成之前 Class Component 中的生命周期的職責。它返回的函數的執行時機如下:
首次渲染不會進行清理,會在下一次渲染,清除上一次的副作用。
卸載階段也會執行清除操作。
不管怎樣,返回值是異步,這樣我們無法預知代碼的執行情況,很容易出現難以定位的 Bug。所以 React 就直接限制了不能 useEffect 回調函數中不能支持 async...await...
useEffect 怎么支持 async...await...
竟然 useEffect 的回調函數不能使用 async...await,那我直接在它內部使用。
做法一:創建一個異步函數(async...await 的方式),然后執行該函數。
useEffect(() => { const asyncFun = async () => { setPass(await mockCheck()); }; asyncFun(); }, []);
做法二:也可以使用 IIFE,如下所示:
useEffect(() => { (async () => { setPass(await mockCheck()); })(); }, []);
自定義 hooks
現在知道解決方法,我們完全可以將其封裝成一個 hook,是不是更優雅。現在看下 ahooks 的 useAsyncEffect,它支持所有的異步寫法,包括 generator function。
思路跟上面一樣,入參跟 useEffect 一樣,一個回調函數(不過這個回調函數支持異步),另外一個依賴項 deps。內部還是 useEffect,將異步的邏輯放入到它的回調函數里面。
function useAsyncEffect( effect: () => AsyncGenerator<void, void, void> | Promise<void>, // 依賴項 deps?: DependencyList, ) { // 判斷是 AsyncGenerator function isAsyncGenerator( val: AsyncGenerator<void, void, void> | Promise<void>, ): val is AsyncGenerator<void, void, void> { // Symbol.asyncIterator: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator // Symbol.asyncIterator 符號指定了一個對象的默認異步迭代器。如果一個對象設置了這個屬性,它就是異步可迭代對象,可用于for await...of循環。 return isFunction(val[Symbol.asyncIterator]); } useEffect(() => { const e = effect(); // 這個標識可以通過 yield 語句可以增加一些檢查點 // 如果發現當前 effect 已經被清理,會停止繼續往下執行。 let cancelled = false; // 執行函數 async function execute() { // 如果是 Generator 異步函數,則通過 next() 的方式全部執行 if (isAsyncGenerator(e)) { while (true) { const result = await e.next(); // Generate function 全部執行完成 // 或者當前的 effect 已經被清理 if (result.done || cancelled) { break; } } } else { await e; } } execute(); return () => { // 當前 effect 已經被清理 cancelled = true; }; }, deps); }
async...await 其實之前就有說道,重點看看實現中變量 cancelled 的實現的功能。 它的作用是中斷執行。
通過 yield 語句可以增加一些檢查點,如果發現當前 effect 已經被清理,會停止繼續往下執行。
現在我們試想,用戶頻繁的操作,可能現在這一輪操作 a 執行還沒完成,就已經開始開始下一輪操作 b。這個時候,操作 a 的邏輯已經失去了作用了,那么我們就可以停止往后執行,直接進入下一輪操作 b 的邏輯執行。這個 cancelled 就是用來取消當前正在執行的一個標識符。
還可以支持 useEffect 的清除機制么?
可以看到上面的 useAsyncEffect,內部的 useEffect 返回函數只返回了如下:
return () => { // 當前 effect 已經被清理 cancelled = true; };
上面其實就是說明,在通過 useAsyncEffect 沒有 useEffect 返回函數中執行清除副作用的功能。
你猜想將 effect(useAsyncEffect的回調函數)的結果,放入到 useAsyncEffect 中不就可以了?
實現最終類似如下:
function useAsyncEffect(effect: () => Promise<void | (() => void)>, dependencies?: any[]) { return useEffect(() => { const cleanupPromise = effect() return () => { cleanupPromise.then(cleanup => cleanup && cleanup()) } }, dependencies) }
上面方法很多大神讀贊成:
上面不是延遲機制,是一種取消機制。否則,在鉤子已經被取消之后,回調函數仍然有機會對外部狀態產生影響。他的實現和例子我也貼一下,跟 useAsyncEffect 其實思路是一樣的,如下:
實現:
function useAsyncEffect(effect: (isCanceled: () => boolean) => Promise<void>, dependencies?: any[]) { return useEffect(() => { let canceled = false; effect(() => canceled); return () => { canceled = true; } }, dependencies) }
Demo:
useAsyncEffect(async (isCanceled) => { const result = await doSomeAsyncStuff(stuffId); if (!isCanceled()) { // TODO: Still OK to do some effect, useEffect hasn't been canceled yet. } }, [stuffId]);
其實歸根結底,我們的清除機制不應該依賴于異步函數,否則很容易出現難以定位的 bug。
總結與思考
在代碼運行中由于 useEffect 是在函數式組件中承擔執行副作用操作的職責,它的返回值的執行操作應該是可以預期的,而不能是一個異步函數,所以不支持回調函數 async...await 的寫法。
我們可以將 async...await 的邏輯封裝在 useEffect 回調函數的內部,這就是 ahooks useAsyncEffect 的實現思路,而且它的范圍更加廣,它支持的是所有的異步函數,包括generator function。
本文內容已結束,歡迎閱讀更多精彩內容。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/128371.html
摘要:現在,請求數據和查詢參數兩個相互獨立,但是我們需要像一個辦法希望他們耦合起來,只獲取輸入框輸入的參數指定的話題文章。好了,現在一旦你改變輸入框內容,數據就會重新獲取。 showImg(https://segmentfault.com/img/remote/1460000018652592?w=1024&h=683); 通過這個教程,我想告訴你在 React 中如何使用 state 和 ...
摘要:在這種情況下,如果狀態發生變化,將再次運行以從獲取數據。你可以在內做到在表單中獲取數據到目前為止,我們只有和按鈕的組合。現在,在獲取數據時,可以使用向函數發送信息。例如,在成功請求的情況下,用于設置新狀態對象的數據。 原文鏈接: https://www.robinwieruch.de/r... 在本教程中,我想通過state和effect hook來像你展示如何用React Hook...
摘要:到目前為止,表達這種流程的基本形式是課程。按鈕依次響應并更改獲取更新的文本。事實證明不能從返回一個。可以在組件中使用本地狀態,而無需使用類。替換了提供統一,和。另一方面,跟蹤中的狀態變化確實很難。 備注:為了保證的可讀性,本文采用意譯而非直譯。 在這個 React鉤子 教程中,你將學習如何使用 React鉤子,它們是什么,以及我們為什么這樣做! showImg(https://segm...
useEffect是很常見的,現在寫的是十分需求的。 useEffect(async()=>{ awaitgetPoiInfo();//請求數據 },[]); 可是React 卻無法支持這樣做,就是因為 effect function 應該返回一個銷毀函數(effect:是指return返回的cleanup函數),如果 useEffect 第一個參數傳入 async,返回值則...
閱讀 547·2023-03-27 18:33
閱讀 732·2023-03-26 17:27
閱讀 630·2023-03-26 17:14
閱讀 591·2023-03-17 21:13
閱讀 521·2023-03-17 08:28
閱讀 1801·2023-02-27 22:32
閱讀 1292·2023-02-27 22:27
閱讀 2178·2023-01-20 08:28