亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

React18中useMemo、useCallback和memo怎么使用

發布時間:2023-05-06 15:24:14 來源:億速云 閱讀:164 作者:iii 欄目:開發技術

本篇內容介紹了“React18中useMemo、useCallback和memo怎么使用”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

用法

useMemo

useMemo 是一個用于優化性能的 React 鉤子。它可以幫助我們避免在組件重新渲染時執行昂貴的計算。useMemo 接受兩個參數:一個函數和一個依賴數組。當依賴數組中的值發生變化時,useMemo 會重新計算并返回新的值。否則,它將返回上一次計算的值。

一個簡單的例子:

import React, { useMemo } from "react";
function ExpensiveComponent({ a, b }) {
  const result = useMemo(() => {
    console.log("Expensive calculation...");
    return a * b;
  }, [a, b]);
  return <div>Result: {result}</div>;
}

我們創建了一個名為 ExpensiveComponent 的組件,它接受兩個屬性 ab 并使用 useMemo 鉤子來計算 ab 的乘積。當 ab 發生變化時,useMemo 會重新計算結果。否則,它將返回上一次計算的值,避免了不必要的計算。

useCallback

useCallback 是另一個用于優化性能的 React 鉤子。它可以幫助我們避免在組件重新渲染時創建新的函數實例。useCallback 接受兩個參數:一個函數和一個依賴數組。當依賴數組中的值發生變化時,useCallback 會返回一個新的函數實例。否則,它將返回上一次創建的函數實例。

再看一個簡單的例子:

import React, { useCallback } from "react";
function ButtonComponent({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}
function ParentComponent() {
  const handleClick = useCallback(() => {
    console.log("Button clicked");
  }, []);
  return (
    <div>
      <ButtonComponent onClick={handleClick}>Click me</ButtonComponent>
    </div>
  );
}

在這個例子中,我們創建了一個名為 ButtonComponent 的組件,它接受一個 onClick 函數屬性。我們還創建了一個名為 ParentComponent 的組件,它使用 useCallback 鉤子來創建一個 handleClick 函數。當 ParentComponent 重新渲染時,useCallback 會返回上一次創建的 handleClick 函數實例,避免了不必要的函數創建。

memo

memo 是一個用于優化性能的 React 高階組件。它可以幫助我們避免在父組件重新渲染時重新渲染子組件。memo 接受一個組件作為參數,并返回一個新的組件。當新組件的屬性發生變化時,它會重新渲染。否則,它將跳過渲染并返回上一次渲染的結果。

繼續舉例子:

import React, { memo } from "react";
const ChildComponent = memo(function ChildComponent({ text }) {
  console.log("ChildComponent rendered");
  return <div>{text}</div>;
});
function ParentComponent({ showChild }) {
  return (
    <div>
      {showChild && <ChildComponent text="Hello, world!" />}
      <button onClick={() => setShowChild(!showChild)}>Toggle child</button>
    </div>
  );
}

在這個例子中,我們創建了一個名為 ChildComponent 的組件,并使用 memo 高階組件對其進行了優化。我們還創建了一個名為 ParentComponent 的組件,它可以切換 ChildComponent 的顯示。當 ParentComponent 重新渲染時,ChildComponent 的屬性沒有發生變化,因此它不會重新渲染。

區別

用法都很清楚了,接下來總結一下它們之間的區別:

  • useMemo 用于避免在組件重新渲染時執行昂貴的計算,只有在依賴發生變化時重新計算值。

  • useCallback 用于避免在組件重新渲染時創建新的函數實例,只有在依賴發生變化時返回新的函數實例。

  • memo 用于避免在父組件重新渲染時重新渲染子組件,只有在屬性發生變化時重新渲染組件。

雖然這些功能都可以幫助我們優化性能,但它們的使用場景和工作原理有所不同。在實際開發中,需要因地制宜合理選用。

源碼分析

為了更深入地了解 useMemouseCallbackmemo 的工作原理,我們將繼續分析 React 18 的源碼。我們將關注這些功能的核心邏輯,并詳細解釋它們的功能。

調度器

眾所周知,在React hooks的體系中,每個鉤子都有自己各個階段的執行邏輯,并且存到對應的Dispatcher中。

就拿useMemo來舉例:

// 掛載時的調度器
const HooksDispatcherOnMount: Dispatcher = {
  // useMemo 掛載時的執行函數
  useMemo: mountMemo,
  // other hooks...
};
// 數據更新時的調度器
const HooksDispatcherOnUpdate: Dispatcher = {
  // useMemo 掛載時的執行函數
  useMemo: updateMemo,
  // other hooks...
};
// 其他生命周期調度器...

上面代碼可以看出,useMemo 在掛載時執行了的是 mountMemo, 而在更新數據時執行的是 updateMemo。但為了更好了解 useMemouseCallbackmemo 的區別,我們只看更新部分就足夠了。

useMemo 源碼分析

源碼在packages/react-reconciler/src/ReactFiberHooks.js 中可以找到:

function updateMemo<T>(
  nextCreate: () => T,
  deps: Array<mixed> | void | null,
): T {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  // Assume these are defined. If they're not, areHookInputsEqual will warn.
  if (nextDeps !== null) {
    const prevDeps: Array<mixed> | null = prevState[1];
    if (areHookInputsEqual(nextDeps, prevDeps)) {
      return prevState[0];
    }
  }
  if (shouldDoubleInvokeUserFnsInHooksDEV) {
    nextCreate();
  }
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

updateMemo 的實現中,有一個關鍵函數 areHookInputsEqual,它用于比較依賴項數組:

function areHookInputsEqual(
  nextDeps: Array<mixed>,
  prevDeps: Array<mixed> | null,
): boolean {
  if (__DEV__) {
    if (ignorePreviousDependencies) {
      // Only true when this component is being hot reloaded.
      return false;
    }
  }
  if (prevDeps === null) {
    if (__DEV__) {
      console.error(
        '%s received a final argument during this render, but not during ' +
          'the previous render. Even though the final argument is optional, ' +
          'its type cannot change between renders.',
        currentHookNameInDev,
      );
    }
    return false;
  }
  if (__DEV__) {
    // Don't bother comparing lengths in prod because these arrays should be
    // passed inline.
    if (nextDeps.length !== prevDeps.length) {
      console.error(
        'The final argument passed to %s changed size between renders. The ' +
          'order and size of this array must remain constant.\n\n' +
          'Previous: %s\n' +
          'Incoming: %s',
        currentHookNameInDev,
        `[${prevDeps.join(', ')}]`,
        `[${nextDeps.join(', ')}]`,
      );
    }
  }
  // $FlowFixMe[incompatible-use] found when upgrading Flow
  for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
    // $FlowFixMe[incompatible-use] found when upgrading Flow
    if (is(nextDeps[i], prevDeps[i])) {
      continue;
    }
    return false;
  }
  return true;
}

areHookInputsEqual 函數接受兩個依賴項數組 nextDepsprevDeps。它首先檢查兩個數組的長度是否相等,如果不相等,將在開發模式下發出警告。然后,它遍歷數組并使用 is 函數(類似于 Object.is)逐個比較元素。如果發現任何不相等的元素,函數將返回 false。否則,返回 true

這個函數在 useMemo 的實現中起到了關鍵作用,因為它決定了是否需要重新計算值。如果依賴項數組相等,useMemo 將返回上一次計算的值;否則,它將執行 nextCreate 函數并返回一個新的值。

useCallback 源碼分析

由于 useCallbackuseMemo 實現一致,其原理都是通過areHookInputsEqual 函數進行依賴項比對,區別在于 useMemo 返回是新數據對象,而 useCallback 返回是回調函數。源碼如下:

function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  if (nextDeps !== null) {
    const prevDeps: Array<mixed> | null = prevState[1];
    if (areHookInputsEqual(nextDeps, prevDeps)) {
      return prevState[0];
    }
  }
  hook.memoizedState = [callback, nextDeps];
  return callback;
}

memo 源碼分析

memo 的實現中,有一個關鍵函數 updateMemoComponent,它用于更新 memo 組件。這個函數位于 packages/react-reconciler/src/ReactFiberBeginWork.js 文件中:

function updateMemoComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  updateLanes: Lanes,
  renderLanes: Lanes,
): null | Fiber {
  if (current !== null) {
    // ...
    const prevProps = current.memoizedProps;
    const compare = Component.compare;
    const compareFn = compare !== null ? compare : shallowEqual;
    if (compareFn(prevProps, nextProps)) {
      return bailoutOnAlreadyFinishedWork(
        current,
        workInProgress,
        renderLanes,
      );
    }
  }
  // ...render the component and return the result
}

updateMemoComponent 函數首先檢查當前組件是否具有上一次的屬性 prevProps。如果存在,它將獲取 memo 組件的比較函數 compare。如果沒有提供比較函數,React 將使用默認的淺比較函數 shallowEqual

接下來,React 使用比較函數來檢查上一次的屬性 prevProps 是否與新的屬性 nextProps 相等。如果相等,React 將調用 bailoutOnAlreadyFinishedWork 函數來阻止組件重新渲染。否則,它將繼續渲染組件并返回結果。

bailoutOnAlreadyFinishedWork 函數的實現位于同一個文件中,它的核心邏輯如下:

    function bailoutOnAlreadyFinishedWork(
      current: Fiber | null,
      workInProgress: Fiber,
      renderLanes: Lanes,
    ): null | Fiber {
      if (current !== null) {
        // Reuse previous dependencies
        workInProgress.dependencies = current.dependencies;
      }
      // ...some code
      // Check if the children have any pending work
      if ((workInProgress.childLanes & renderLanes) !== NoLanes) {
        // ...some code
      } else {
        // The children don't have any work. Set the bailout state.
        workInProgress.lanes = NoLanes;
        workInProgress.childLanes = NoLanes;
        return null;
      }
      // ...some code
    }

bailoutOnAlreadyFinishedWork 函數首先復用上一次的依賴項。然后,它檢查子組件是否有任何待處理的工作。如果沒有,它將設置 workInProgress.lanesworkInProgress.childLanesNoLanes,并返回 null,從而阻止組件重新渲染。

“React18中useMemo、useCallback和memo怎么使用”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

惠东县| 得荣县| 剑阁县| 定结县| 尉犁县| 阳西县| 漳州市| 赤城县| 阿拉尔市| 玉树县| 蓝田县| 筠连县| 天门市| 依安县| 略阳县| 郓城县| 两当县| 望奎县| 建水县| 保亭| 安国市| 丽水市| 保康县| 高陵县| 班玛县| 石景山区| 平湖市| 平原县| 英山县| 靖江市| 丰城市| 和林格尔县| 迭部县| 南阳市| 怀仁县| 潼关县| 繁昌县| 宁晋县| 万山特区| 安阳市| 庆阳市|