最近嘗試在 useEffect 使用 async 的時候會報錯,因此,本篇文章就是想喝大家說說為什么?也解讀其中緣由。
具體代碼分析
執(zhí)行 mountEffect
當頁面中使用 useEffect 的時候,會在初始化的時候執(zhí)行 mountEffect 如下:
useEffect: function(create, deps) { currentHookNameInDev = "useEffect"; mountHookTypesDev(); checkDepsAreArrayDev(deps); return mountEffect(create, deps); },
執(zhí)行 mountEffectImpl
執(zhí)行 mountEffect 的時候執(zhí)行 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); }
執(zhí)行 pushEffect
在 pushEffect 中會創(chuàng)建一個 effect 節(jié)點,也要哦添加到當前函數(shù)對應(yīng) fiber 的 updateQueue 上面,數(shù)據(jù)結(jié)構(gòu)是一個環(huán)鏈。
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
直接忽略中間調(diào)度代碼內(nèi)容,進入到 schedulePassiveEffects,這個函數(shù)作用是從函數(shù)組件對應(yīng)的 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; }); } }
invokePassiveEffectCreate 執(zhí)行
早一大推度之后會進入 flushPassiveEffectsImpl ,函數(shù)太長了,只貼出相關(guān)的部分,邏輯是循環(huán)掛載 effect 隊列中的每一個 effect 傳入到 invokePassiveEffectCreate 執(zhí)行
// ... 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(); } } // ...
這個函數(shù)會獲取 create 并執(zhí)行,然后將執(zhí)行結(jié)果掛載到 destroy 上,這里的 create 就是 useEffect 中的第一個參數(shù),從這里可以看出,如果有返回值,那么 destroy 就是第一個函數(shù)的返回值,沒有就是 undefined
function invokePassiveEffectCreate(effect) { var create = effect.create; effect.destroy = create(); }
卸載的時候會通過函數(shù)組件對應(yīng)的 fiber 獲取 effect 鏈表,然后遍歷鏈表,獲取環(huán)鏈上的每一個節(jié)點,如果 destroy 不是 undefined 就執(zhí)行,所以如果 useEffect 第一個參數(shù)傳入 async, 那么這里的 destroy 就是一個 promise 對象,對象是不能執(zhí)行的,所以報錯。
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 實現(xiàn)如下。
hook 實現(xiàn)
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,這就是留給大家開動腦筋解決。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/128306.html
背景 在使用useEffect中用啦回調(diào)函數(shù)中使用 async...await... 這時候就會報錯?! ∩厦娲a可以看到,在報錯,effect function 應(yīng)該返回一個銷毀函數(shù)(effect:是指return返回的cleanup函數(shù)),如果 useEffect 第一個參數(shù)傳入 async,返回值則變成了 Promise,結(jié)果就是會導(dǎo)致 react 在調(diào)用銷毀函數(shù)的時候報錯。 React...
摘要:到目前為止,表達這種流程的基本形式是課程。按鈕依次響應(yīng)并更改獲取更新的文本。事實證明不能從返回一個??梢栽诮M件中使用本地狀態(tài),而無需使用類。替換了提供統(tǒng)一,和。另一方面,跟蹤中的狀態(tài)變化確實很難。 備注:為了保證的可讀性,本文采用意譯而非直譯。 在這個 React鉤子 教程中,你將學(xué)習(xí)如何使用 React鉤子,它們是什么,以及我們?yōu)槭裁催@樣做! showImg(https://segm...
摘要:現(xiàn)在,請求數(shù)據(jù)和查詢參數(shù)兩個相互獨立,但是我們需要像一個辦法希望他們耦合起來,只獲取輸入框輸入的參數(shù)指定的話題文章。好了,現(xiàn)在一旦你改變輸入框內(nèi)容,數(shù)據(jù)就會重新獲取。 showImg(https://segmentfault.com/img/remote/1460000018652592?w=1024&h=683); 通過這個教程,我想告訴你在 React 中如何使用 state 和 ...
摘要:另外也不利于組件的,及。所以在使用時,盡量將相關(guān)聯(lián)的,會共同變化的值放入一個。有同學(xué)可能會想,每次后都會執(zhí)行,這樣會不會對性能造成影響。另外必須以開頭來命名,這樣工具才能正確檢測其是否符合規(guī)范。 由于工作的原因我已經(jīng)很長時間沒接觸過React了。前段時間圈子里都在討論React Hooks,出于好奇也學(xué)習(xí)了一番,特此整理以加深理解。 緣由 在web應(yīng)用無所不能的9012年,組成應(yīng)用的C...
閱讀 773·2023-03-27 18:33
閱讀 1001·2023-03-26 17:27
閱讀 889·2023-03-26 17:14
閱讀 826·2023-03-17 21:13
閱讀 756·2023-03-17 08:28
閱讀 2229·2023-02-27 22:32
閱讀 1632·2023-02-27 22:27
閱讀 2595·2023-01-20 08:28