为什么在useEffect中使用async会报错?
最近在使用useEffect的时候遇到了一个问题,当我尝试在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中会创建一个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);
}
}
在invokePassiveEffectCreate执行的时候,会获取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,包裹一下就可以处理这个问题了,代码如下:
import { useEffect } from 'react';
export default function useAsyncEffect<T, U extends any[]>(method: () => Promise<T>, deps: U) {
useEffect(() => {
(async () => {
await method()
})()
}, deps)
}
使用的时候,直接将需要使用async的useEffect替换成useAsyncEffect即可:
import React, { useState } from 'react';
import useAsyncEffect from './use
原创文章,作者:小编小本本,如若转载,请注明出处:https://www.benjiyun.com/yunzhujiyunwei/vps-yunwei/7211.html
