您好,登錄后才能下訂單哦!
本篇內容介紹了“useEffect中不能使用async的原理是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
當頁面中使用 useEffect 的時候,會在初始化的時候執行 mountEffect 如下:
useEffect: function(create, deps) { currentHookNameInDev = "useEffect"; mountHookTypesDev(); checkDepsAreArrayDev(deps); return mountEffect(create, deps); },
執行 mountEffect 的時候執行 mountEffectImpl 如下:
function mountEffectImpl(fiberFlags, hookFlags, create, deps) { var hook = mountWorkInProgressHook(); var nextDeps = deps === void 0 ? null : deps; currentlyRenderingFiber$1.flags |= fiberFlags; hook.memoizedState = pushEffect(HasEffect | hookFlags, create, void 0, nextDeps); }
之后執行 pushEffect,在 pushEffect 中會創建一個 effect 節點,然后添加到當前函數對應 fiber 的 updateQueue 上面,數據結構是一個環鏈。
function pushEffect(tag, create, destroy, deps) { var effect = { tag, create, destroy, deps, next: null }; var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue; if (componentUpdateQueue === null) { componentUpdateQueue = createFunctionComponentUpdateQueue(); currentlyRenderingFiber$1.updateQueue = componentUpdateQueue; componentUpdateQueue.lastEffect = effect.next = effect; } else { var lastEffect = componentUpdateQueue.lastEffect; if (lastEffect === null) { componentUpdateQueue.lastEffect = effect.next = effect; } else { var firstEffect = lastEffect.next; lastEffect.next = effect; effect.next = firstEffect; componentUpdateQueue.lastEffect = effect; } } return effect; }
中間又是一大堆調度,協調的邏輯,不是我們關注的重點,這里省略掉直接進入到 schedulePassiveEffects,這個函數作用是從函數組件對應的 fiber 上獲取上面掛載的 effect,然后將 effect 和 fiber 堆到 pendingPassiveHookEffectsUnmount 和 pendingPassiveHookEffectsMount 這個兩個隊列中
function schedulePassiveEffects(finishedWork) { var updateQueue = finishedWork.updateQueue; var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null; if (lastEffect !== null) { var firstEffect = lastEffect.next; var effect = firstEffect; do { var _effect = effect , next = _effect.next , tag = _effect.tag; if ((tag & Passive$1) !== NoFlags$1 && (tag & HasEffect) !== NoFlags$1) { // enqueuePendingPassiveHookEffectUnmount(finishedWork, effect); enqueuePendingPassiveHookEffectMount(finishedWork, effect); } effect = next; } while (effect !== firstEffect); } }
這里是推入的邏輯,只展示推入掛載隊列的方法,推入卸載隊列是一樣的
function enqueuePendingPassiveHookEffectMount(fiber, effect) { pendingPassiveHookEffectsMount.push(effect, fiber); if (!rootDoesHavePassiveEffects) { rootDoesHavePassiveEffects = true; scheduleCallback(NormalPriority$1, function() { flushPassiveEffects(); return null; }); } }
之后又是一大推調度,協調的邏輯,等待協調執行完畢后,之后會進入 flushPassiveEffectsImpl ,函數太長了,只貼出相關的部分,邏輯是循環掛載 effect 隊列中的每一個 effect 傳入到 invokePassiveEffectCreate 執行
// ... var mountEffects = pendingPassiveHookEffectsMount; pendingPassiveHookEffectsMount = []; for (var _i = 0; _i < mountEffects.length; _i += 2) { var _effect2 = mountEffects[_i]; var _fiber = mountEffects[_i + 1]; { setCurrentFiber(_fiber); { invokeGuardedCallback(null, invokePassiveEffectCreate, null, _effect2); } if (hasCaughtError()) { if (!(_fiber !== null)) { { throw Error("Should be working on an effect."); } } var _error4 = clearCaughtError(); captureCommitPhaseError(_fiber, _error4); } resetCurrentFiber(); } } // ...
這個函數會獲取 create 并執行,然后將執行結果掛載到 destroy 上,這里的 create 就是 useEffect 中的第一個參數,從這里可以看出,如果有返回值,那么 destroy 就是第一個函數的返回值,沒有就是 undefined
function invokePassiveEffectCreate(effect) { var create = effect.create; effect.destroy = create(); }
卸載的時候會通過函數組件對應的 fiber 獲取 effect 鏈表,然后遍歷鏈表,獲取環鏈上的每一個節點,如果 destroy 不是 undefined 就執行,所以如果 useEffect 第一個參數傳入 async, 那么這里的 destroy 就是一個 promise 對象,對象是不能執行的,所以報錯。
function commitHookEffectListUnmount(tag, finishedWork) { var updateQueue = finishedWork.updateQueue; var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null; if (lastEffect !== null) { var firstEffect = lastEffect.next; var effect = firstEffect; do { if ((effect.tag & tag) === tag) { // Unmount var destroy = effect.destroy; effect.destroy = undefined; if (destroy !== undefined) { destroy(); } } effect = effect.next; } while (effect !== firstEffect); } }
既然知道了原因那么,解決方案就非常簡單,直接手寫一個自定義 hook,包裹一下就可以處理這個問題了,hook 實現如下。
import { useEffect } from 'react' export default function useAsyncEffect<T, U extends any[]>( method: () => Promise<T>, deps: U ) { useEffect(() => { (async () => { await method() })() }, deps) }
使用:
import React, { useState } from 'react' import { useAsyncEffect } from './useAsyncEffect' export default function Demo() { const [count, setCount] = useState(0) function fetchData(): Promise<number> { return new Promise((resolve) => { setTimeout(() => { resolve(count + 1) }, 2000) }) } useAsyncEffect(async () => { const count = await fetchData() setCount(count) }, [fetchData]) return ( <div>{count}</div> ) }
這里其實有問題,因為返回值永遠是undefined,你可以開動腦筋嘗試修復一下。
“useEffect中不能使用async的原理是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。