本篇内容介绍了“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的原理是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!