欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

useEffect中不能使用async原理詳解

 更新時間:2022年07月11日 10:28:04   作者:東都花神  
這篇文章主要為大家介紹了useEffect中為什么不能使用async的原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

當我們嘗試在 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)度,協(xié)調(diào)的邏輯,不是我們關(guān)注的重點,這里省略掉直接進入到 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í)行

之后又是一大推調(diào)度,協(xié)調(diào)的邏輯,等待協(xié)調(diào)執(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,你可以開動腦筋嘗試修復(fù)一下。

以上就是對 useEffect 中為啥不能使用 async 的簡單分析,有不足之處,歡迎提出,更多關(guān)于useEffect不能使用async的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 解讀useState第二個參數(shù)的"第二個參數(shù)"

    解讀useState第二個參數(shù)的"第二個參數(shù)"

    這篇文章主要介紹了useState第二個參數(shù)的"第二個參數(shù)",具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • react-router中<Link/>的屬性詳解

    react-router中<Link/>的屬性詳解

    這篇文章主要給大家介紹了關(guān)于react-router中<Link/>屬性的相關(guān)資料,文中介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。
    2017-06-06
  • 基于PixiJS實現(xiàn)react圖標旋轉(zhuǎn)動效

    基于PixiJS實現(xiàn)react圖標旋轉(zhuǎn)動效

    PixiJS是一個開源的基于web的渲染系統(tǒng),為游戲、數(shù)據(jù)可視化和其他圖形密集型項目提供了極快的性能,這篇文章主要介紹了用PixiJS實現(xiàn)react圖標旋轉(zhuǎn)動效,需要的朋友可以參考下
    2022-05-05
  • React中refs的一些常見用法匯總

    React中refs的一些常見用法匯總

    Refs是一個 獲取 DOM節(jié)點或React元素實例的工具,在React中Refs 提供了一種方式,允許用戶訪問DOM 節(jié)點或者在render方法中創(chuàng)建的React元素,這篇文章主要給大家介紹了關(guān)于React中refs的一些常見用法,需要的朋友可以參考下
    2021-07-07
  • 淺談React中組件間抽象

    淺談React中組件間抽象

    這篇文章主要介紹了淺談React中組件間抽象,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • React的特征單向數(shù)據(jù)流學習

    React的特征單向數(shù)據(jù)流學習

    這篇文章主要為大家介紹了React的特征單向數(shù)據(jù)流學習,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • react-redux的connect用法詳解

    react-redux的connect用法詳解

    react-redux是react官方推出的redux綁定庫,React-Redux 將所有組件分成兩大類一個是UI組件和容器組件,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-01-01
  • 基于React封裝組件的實現(xiàn)步驟

    基于React封裝組件的實現(xiàn)步驟

    很多小伙伴在第一次嘗試封裝組件時會和我一樣碰到許多問題,本文主要介紹了基于React封裝組件的實現(xiàn)步驟,感興趣的可以了解一下
    2021-11-11
  • React函數(shù)組件和類組件的區(qū)別及說明

    React函數(shù)組件和類組件的區(qū)別及說明

    這篇文章主要介紹了React函數(shù)組件和類組件的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • React實現(xiàn)復(fù)雜搜索表單的展開收起功能

    React實現(xiàn)復(fù)雜搜索表單的展開收起功能

    本節(jié)對于需要展開收起效果的查詢表單進行概述,主要涉及前端樣式知識。對React實現(xiàn)復(fù)雜搜索表單的展開-收起功能感興趣的朋友一起看看吧
    2021-09-09

最新評論