温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

useEffect中不能使用async的原理是什么

发布时间:2022-07-11 13:50:41 来源:亿速云 阅读:225 作者:iii 栏目:开发技术

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

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI