React官網Hook學習——下篇


本文記錄的是React官網部分的內置Hook學習第二部分

10、useInsertionEffect

useInsertionEffect 可以在佈局副作用觸發之前將元素插入到 DOM 中。

useInsertionEffect(setup, dependencies?)
11、useLayoutEffect

useLayoutEffect 可能會影響性能。儘可能使用 useEffect。

useLayoutEffect 是 useEffect 的一個版本,在瀏覽器重新繪製屏幕之前觸發。

useLayoutEffect(setup, dependencies?)
參考

useLayoutEffect(setup, dependencies?)

調用 useLayoutEffect 在瀏覽器重新繪製屏幕之前進行佈局測量:

import { useState, useRef, useLayoutEffect } from 'react';

function Tooltip() {
  const ref = useRef(null);
  const [tooltipHeight, setTooltipHeight] = useState(0);

  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTooltipHeight(height);
  }, []);
  // ...
參數
  • setup:處理副作用的函數。setup 函數選擇性返回一個清理(cleanup)函數。在將組件首次添加到 DOM 之前,React 將運行 setup 函數。在每次因為依賴項變更而重新渲染後,React 將首先使用舊值運行 cleanup 函數(如果你提供了該函數),然後使用新值運行 setup 函數。在組件從 DOM 中移除之前,React 將最後一次運行 cleanup 函數。
  • 可選 dependenciessetup 代碼中引用的所有響應式值的列表。響應式值包括 props、state 以及所有直接在組件內部聲明的變量和函數。如果你的代碼檢查工具 配置了 React,那麼它將驗證每個響應式值都被正確地指定為一個依賴項。依賴項列表必須具有固定數量的項,並且必須像 [dep1, dep2, dep3] 這樣內聯編寫。React 將使用 Object.is 來比較每個依賴項和它先前的值。如果省略此參數,則在每次重新渲染組件之後,將重新運行副作用函數。
返回值

useLayoutEffect 返回 undefined

注意事項
  • useLayoutEffect 是一個 Hook,因此只能在 組件的頂層 或自己的 Hook 中調用它。不能在循環或者條件內部調用它。如果你需要的話,抽離出一個組件並將副作用處理移動到那裏。
  • 當 StrictMode 啓用時,React 將在真正的 setup 函數首次運行前,運行一個額外的開發專有的 setup + cleanup 週期。這是一個壓力測試,確保 cleanup 邏輯“映照”到 setup 邏輯,並停止或撤消 setup 函數正在做的任何事情。如果這導致一個問題,請實現清理函數。
  • 如果你的一些依賴項是組件內部定義的對象或函數,則存在這樣的風險,即它們將 導致 Effect 重新運行的次數多於所需的次數。要解決這個問題,請刪除不必要的 對象 和 函數 依賴項。你還可以 抽離狀態更新 和 非響應式邏輯 到 Effect 之外。
  • Effect 只在客户端上運行,在服務端渲染中不會運行。
  • useLayoutEffect 內部的代碼和所有計劃的狀態更新阻塞了瀏覽器重新繪製屏幕。如果過度使用,這會使你的應用程序變慢。如果可能的話,儘量選擇 useEffect。
  • 如果你在 useLayoutEffect 內部觸發狀態更新,React 將立即執行所有剩餘的 Effects,包括 useEffect
用法
在瀏覽器重新繪製屏幕前計算佈局

大多數組件不需要依靠它們在屏幕上的位置和大小來決定渲染什麼。他們只返回一些 JSX,然後瀏覽器計算他們的 佈局(位置和大小)並重新繪製屏幕。

有時候,這還不夠。想象一下懸停時出現在某個元素旁邊的 tooltip。如果有足夠的空間,tooltip 應該出現在元素的上方,但是如果不合適,它應該出現在下面。為了讓 tooltip 渲染在最終正確的位置,你需要知道它的高度(即它是否適合放在頂部)。

要做到這一點,你需要分兩步渲染:

  1. 將 tooltip 渲染到任何地方(即使位置不對)。
  2. 測量它的高度並決定放置 tooltip 的位置。
  3. 把 tooltip 渲染放在正確的位置。

所有這些都需要在瀏覽器重新繪製屏幕之前完成。你不希望用户看到 tooltip 在移動。

下面是這如何一步步工作的:

  1. Tooltip 使用初始值 tooltipHeight = 0 進行渲染(因此 tooltip 可能被錯誤地放置)。
  2. React 將它放在 DOM 中,然後運行 useLayoutEffect 中的代碼。
  3. useLayoutEffect 測量 了 tooltip 內容的高度,並立即觸發重新渲染。
  4. 使用實際的 tooltipHeight 再次渲染 Tooltip(這樣 tooltip 的位置就正確了)。
  5. React 在 DOM 中對它進行更新,瀏覽器最終顯示出 tooltip。

將鼠標懸停在下面的按鈕上,看看 tooltip 是如何根據它是否合適來調整它的位置:

import { useRef, useLayoutEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import TooltipContainer from './TooltipContainer.js';

export default function Tooltip({ children, targetRect }) {
  const ref = useRef(null);
  const [tooltipHeight, setTooltipHeight] = useState(0);

  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTooltipHeight(height);
    console.log('Measured tooltip height: ' + height);
  }, []);

  let tooltipX = 0;
  let tooltipY = 0;
  if (targetRect !== null) {
    tooltipX = targetRect.left;
    tooltipY = targetRect.top - tooltipHeight;
    if (tooltipY < 0) {
      // 它不適合上方,因此把它放在下面。
      tooltipY = targetRect.bottom;
    }
  }

  return createPortal(
    <TooltipContainer x={tooltipX} y={tooltipY} contentRef={ref}>
      {children}
    </TooltipContainer>,
    document.body
  );
}
12、useMemo

useMemo 是一個 React Hook,它在每次重新渲染的時候能夠緩存計算的結果。

const cachedValue = useMemo(calculateValue, dependencies)
參考

useMemo(calculateValue, dependencies)

在組件的頂層調用 useMemo 來緩存每次重新渲染都需要計算的結果。

import { useMemo } from 'react';

function TodoList({ todos, tab }) {
  const visibleTodos = useMemo(
    () => filterTodos(todos, tab),
    [todos, tab]
  );
  // ...
}
參數
  • calculateValue:要緩存計算值的函數。它應該是一個沒有任何參數的純函數,並且可以返回任意類型。React 將會在首次渲染時調用該函數;在之後的渲染中,如果 dependencies 沒有發生變化,React 將直接返回相同值。否則,將會再次調用 calculateValue 並返回最新結果,然後緩存該結果以便下次重複使用。
  • dependencies:所有在 calculateValue 函數中使用的響應式變量組成的數組。響應式變量包括 props、state 和所有你直接在組件中定義的變量和函數。如果你在代碼檢查工具中 配置了 React,它將會確保每一個響應式數據都被正確地定義為依賴項。依賴項數組的長度必須是固定的並且必須寫成 [dep1, dep2, dep3] 這種形式。React 使用 Object.is 將每個依賴項與其之前的值進行比較。
返回值

在初次渲染時,useMemo 返回不帶參數調用 calculateValue 的結果。

在接下來的渲染中,如果依賴項沒有發生改變,它將返回上次緩存的值;否則將再次調用 calculateValue,並返回最新結果。

注意
  • useMemo 是一個 React Hook,所以你只能 在組件的頂層 或者自定義 Hook 中調用它。你不能在循環語句或條件語句中調用它。如有需要,將其提取為一個新組件並使用 state。
  • 在嚴格模式下,為了 幫你發現意外的錯誤,React 將會 調用你的計算函數兩次。這只是一個開發環境下的行為,並不會影響到生產環境。如果計算函數是一個純函數(它本來就應該是),這將不會影響到代碼邏輯。其中一次的調用結果將被忽略。
  • 除非有特定原因,React 不會丟棄緩存值。例如,在開發過程中,React 會在你編輯組件文件時丟棄緩存。無論是在開發環境還是在生產環境,如果你的組件在初始掛載期間被終止,React 都會丟棄緩存。在未來,React 可能會添加更多利用丟棄緩存的特性——例如,如果 React 在未來增加了對虛擬化列表的內置支持,那麼丟棄那些滾出虛擬化列表視口的緩存是有意義的。你可以僅僅依賴 useMemo 作為性能優化手段。否則,使用 state 變量 或者 ref 可能更加合適。
13、useOptimistic

useOptimistic 是一個 React Hook,它可以幫助你更樂觀地更新用户界面。

const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
參考

useOptimistic(state, updateFn)

useOptimistic 是一個 React Hook,它允許你在進行異步操作時顯示不同 state。它接受 state 作為參數,並返回該 state 的副本,在異步操作(如網絡請求)期間可以不同。你需要提供一個函數,該函數接受當前 state 和操作的輸入,並返回在操作掛起期間要使用的樂觀狀態。

這個狀態被稱為“樂觀”狀態是因為通常用於立即向用户呈現執行操作的結果,即使實際上操作需要一些時間來完成。

import { useOptimistic } from 'react';

function AppContainer() {
  const [optimisticState, addOptimistic] = useOptimistic(
    state,
    // 更新函數
    (currentState, optimisticValue) => {
      // 使用樂觀值
      // 合併並返回新 state
    }
  );
}
參數
  • state:初始時和沒有掛起操作時要返回的值。
  • updateFn(currentState, optimisticValue):一個函數,接受當前 state 和傳遞給 addOptimistic 的樂觀值,並返回結果樂觀狀態。它必須是一個純函數。updateFn 接受兩個參數:currentStateoptimisticValue。返回值將是 currentStateoptimisticValue 的合併值。
返回值
  • optimisticState:結果樂觀狀態。除非有操作掛起,否則它等於 state,在這種情況下,它等於 updateFn 返回的值。
  • addOptimistic:觸發樂觀更新時調用的 dispatch 函數。它接受一個可以是任何類型的參數 optimisticValue,並以 stateoptimisticValue 作為參數來調用 updateFn
用法
樂觀地更新表單

seOptimistic Hook 提供了一種在後台操作(如網絡請求)完成之前樂觀地更新用户界面的方式。在表單的上下文中,這種技術有助於使應用程序在感覺上響應地更加快速。當用户提交表單時,界面立即更新為預期的結果,而不是等待服務器的響應來反映更改。

import { useOptimistic, useState, useRef, startTransition } from "react";
import { deliverMessage } from "./actions.js";

function Thread({ messages, sendMessageAction }) {
  const formRef = useRef();
  function formAction(formData) {
    addOptimisticMessage(formData.get("message"));
    formRef.current.reset();
    startTransition(async () => {
      await sendMessageAction(formData);
    });
  }
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [
      {
        text: newMessage,
        sending: true
      },
      ...state,
    ]
  );

  return (
    <>
      <form action={formAction} ref={formRef}>
        <input type="text" name="message" placeholder="你好!" />
        <button type="submit">發送</button>
      </form>
      {optimisticMessages.map((message, index) => (
        <div key={index}>
          {message.text}
          {!!message.sending && <small>(發送中……)</small>}
        </div>
      ))}
      
    </>
  );
}

export default function App() {
  const [messages, setMessages] = useState([
    { text: "你好,在這兒!", sending: false, key: 1 }
  ]);
  async function sendMessageAction(formData) {
    const sentMessage = await deliverMessage(formData.get("message"));
    startTransition(() => {
      setMessages((messages) => [{ text: sentMessage }, ...messages]);
    })
  }
  return <Thread messages={messages} sendMessageAction={sendMessageAction} />;
}
export async function deliverMessage(message) {
  await new Promise((res) => setTimeout(res, 1000));
  return message;
}
14、useReducer

useReducer 是一個 React Hook,它允許你向組件裏面添加一個 reducer。

const [state, dispatch] = useReducer(reducer, initialArg, init?)
參考

useReducer(reducer, initialArg, init?)

在組件的頂層作用域調用 useReducer 以創建一個用於管理狀態的 reducer。

import { useReducer } from 'react';

function reducer(state, action) {
  // ...
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });
  // ...
參數
  • reducer:用於更新 state 的純函數。參數為 state 和 action,返回值是更新後的 state。state 與 action 可以是任意合法值。
  • initialArg:用於初始化 state 的任意值。初始值的計算邏輯取決於接下來的 init 參數。
  • 可選參數 init:用於計算初始值的函數。如果存在,使用 init(initialArg) 的執行結果作為初始值,否則使用 initialArg
返回值

useReducer 返回一個由兩個值組成的數組:

  1. 當前的 state。初次渲染時,它是 init(initialArg)initialArg (如果沒有 init 函數)。
  2. dispatch。用於更新 state 並觸發組件的重新渲染。
注意事項
  • useReducer 是一個 Hook,所以只能在 組件的頂層作用域 或自定義 Hook 中調用,而不能在循環或條件語句中調用。如果你有這種需求,可以創建一個新的組件,並把 state 移入其中。
  • dispatch 函數具有穩定的標識,所以你經常會看到 Effect 的依賴數組中會省略它,即使包含它也不會導致 Effect 重新觸發。如果 linter 允許你省略依賴項並且沒有報錯,那麼你就可以安全地省略它。瞭解移除 Effect 依賴項的更多信息。
  • 嚴格模式下 React 會 調用兩次 reducer 和初始化函數,這可以 幫助你發現意外的副作用。這只是開發模式下的行為,並不會影響生產環境。只要 reducer 和初始化函數是純函數(理應如此)就不會改變你的邏輯。其中一個調用結果會被忽略。
dispatch 函數

useReducer 返回的 dispatch 函數允許你更新 state 並觸發組件的重新渲染。它需要傳入一個 action 作為參數:

const [state, dispatch] = useReducer(reducer, { age: 42 });

function handleClick() {
  dispatch({ type: 'incremented_age' });
  // ...

React 會調用 reducer 函數以更新 state,reducer 函數的參數為當前的 state 與傳遞的 action。

參數
  • action:用户執行的操作。可以是任意類型的值。通常來説 action 是一個對象,其中 type 屬性標識類型,其它屬性攜帶額外信息。
返回值

dispatch 函數沒有返回值。

注意
  • dispatch 函數 是為下一次渲染而更新 state。因此在調用 dispatch 函數後讀取 state 並不會拿到更新後的值,也就是説只能獲取到調用前的值。
  • 如果你提供的新值與當前的 state 相同(使用 Object.is 比較),React 會 跳過組件和子組件的重新渲染,這是一種優化手段。雖然在跳過重新渲染前 React 可能會調用你的組件,但是這不應該影響你的代碼。
  • React 會批量更新 state。state 會在 所有事件函數執行完畢 並且已經調用過它的 set 函數後進行更新,這可以防止在一個事件中多次進行重新渲染。如果在訪問 DOM 等極少數情況下需要強制 React 提前更新,可以使用 flushSync。
15、useRef

useRef 是一個 React Hook,它能幫助引用一個不需要渲染的值。

const ref = useRef(initialValue)
參考

useRef(initialValue)

在組件頂層調用 useRef 以聲明一個 ref。

import { useRef } from 'react';

function MyComponent() {
  const intervalRef = useRef(0);
  const inputRef = useRef(null);
  // ...
參數
  • initialValue:ref 對象的 current 屬性的初始值。可以是任意類型的值。這個參數在首次渲染後被忽略。
返回值

useRef 返回一個只有一個屬性的對象:

  • current:初始值為傳遞的 initialValue。之後可以將其設置為其他值。如果將 ref 對象作為一個 JSX 節點的 ref 屬性傳遞給 React,React 將為它設置 current 屬性。

在後續的渲染中,useRef 將返回同一個對象。

注意
  • 可以修改 ref.current 屬性。與 state 不同,它是可變的。然而,如果它持有一個用於渲染的對象(例如 state 的一部分),那麼就不應該修改這個對象。
  • 改變 ref.current 屬性時,React 不會重新渲染組件。React 不知道它何時會發生改變,因為 ref 是一個普通的 JavaScript 對象。
  • 除了 初始化 外不要在渲染期間寫入或者讀取 ref.current,否則會使組件行為變得不可預測。
  • 在嚴格模式下,React 將會 調用兩次組件方法,這是為了 幫助發現意外問題。但這只是開發模式下的行為,不會影響生產模式。每個 ref 對象都將會創建兩次,但是其中一個版本將被丟棄。如果使用的是組件純函數(也應當如此),那麼這不會影響其行為。
16、useState

useState 是一個 React Hook,它允許你向組件添加一個 狀態變量。

const [state, setState] = useState(initialState)
參考

useState(initialState)

在組件的頂層調用 useState 來聲明一個 狀態變量。

import { useState } from 'react';
function MyComponent() {
  const [age, setAge] = useState(28);
  const [name, setName] = useState('Taylor');
  const [todos, setTodos] = useState(() => createTodos());
  // ...

按照慣例使用 數組解構 來命名狀態變量,例如 [something, setSomething]

參數
  • initialState:你希望 state 初始化的值。它可以是任何類型的值,但對於函數有特殊的行為。在初始渲染後,此參數將被忽略。
  • 如果傳遞函數作為 initialState,則它將被視為 初始化函數。它應該是純函數,不應該接受任何參數,並且應該返回一個任何類型的值。當初始化組件時,React 將調用你的初始化函數,並將其返回值存儲為初始狀態。
返回

useState 返回一個由兩個值組成的數組:

  1. 當前的 state。在首次渲染時,它將與你傳遞的 initialState 相匹配。
  2. set,它可以讓你將 state 更新為不同的值並觸發重新渲染。
注意事項
  • useState 是一個 Hook,因此你只能在 組件的頂層 或自己的 Hook 中調用它。你不能在循環或條件語句中調用它。如果你需要這樣做,請提取一個新組件並將狀態移入其中。
  • 在嚴格模式中,React 將 兩次調用初始化函數,以 幫你找到意外的不純性。這只是開發時的行為,不影響生產。如果你的初始化函數是純函數(本該是這樣),就不應影響該行為。其中一個調用的結果將被忽略。
set 函數,例如 setSomething(nextState)

useState 返回的 set 函數允許你將 state 更新為不同的值並觸發重新渲染。你可以直接傳遞新狀態,也可以傳遞一個根據先前狀態來計算新狀態的函數:

const [name, setName] = useState('Edward');

function handleClick() {
  setName('Taylor');
  setAge(a => a + 1);
  // ...
參數
  • nextState:你想要 state 更新為的值。它可以是任何類型的值,但對於函數有特殊的行為。
  • 如果你將函數作為 nextState 傳遞,它將被視為 更新函數。它必須是純函數,只接受待定的 state 作為其唯一參數,並應返回下一個狀態。React 將把你的更新函數放入隊列中並重新渲染組件。在下一次渲染期間,React 將通過把隊列中所有更新函數應用於先前的狀態來計算下一個狀態。
返回值

set 函數沒有返回值。

注意事項
  • set 函數 僅更新 *下一次* 渲染的狀態變量。如果在調用 set 函數後讀取狀態變量,則 仍會得到在調用之前顯示在屏幕上的舊值。
  • 如果你提供的新值與當前 state 相同(由 Object.is 比較確定),React 將 跳過重新渲染該組件及其子組件。這是一種優化。雖然在某些情況下 React 仍然需要在跳過子組件之前調用你的組件,但這不應影響你的代碼。
  • React 會 批量處理狀態更新。它會在所有 事件處理函數運行 並調用其 set 函數後更新屏幕。這可以防止在單個事件期間多次重新渲染。在某些罕見情況下,你需要強制 React 更早地更新屏幕,例如訪問 DOM,你可以使用 flushSync。
  • set 函數具有穩定的標識,所以你經常會看到 Effect 的依賴數組中會省略它,即使包含它也不會導致 Effect 重新觸發。如果 linter 允許你省略依賴項並且沒有報錯,那麼你就可以安全地省略它。瞭解移除 Effect 依賴項的更多信息。
  • 在渲染期間,只允許在當前渲染組件內部調用 set 函數。React 將丟棄其輸出並立即嘗試使用新狀態重新渲染。這種方式很少需要,但你可以使用它來存儲 先前渲染中的信息
  • 在嚴格模式中,React 將 兩次調用你的更新函數,以幫助你找到 意外的不純性。這只是開發時的行為,不影響生產。如果你的更新函數是純函數(本該是這樣),就不應影響該行為。其中一次調用的結果將被忽略。
17、useSyncExternalStore

useSyncExternalStore 是一個讓你訂閲外部 store 的 React Hook。

const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
參考

useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)

在組件頂層調用 useSyncExternalStore 以從外部 store 讀取值。

import { useSyncExternalStore } from 'react';
import { todosStore } from './todoStore.js';

function TodosApp() {
  const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);
  // ...
}

它返回 store 中數據的快照。你需要傳兩個函數作為參數:

  1. subscribe 函數應當訂閲該 store 並返回一個取消訂閲的函數。
  2. getSnapshot 函數應當從該 store 讀取數據的快照。
參數
  • subscribe:一個函數,接收一個單獨的 callback 參數並把它訂閲到 store 上。當 store 發生改變時應該調用提供的 callback,這將使 React 重新調用 getSnapshot 並在需要的時候重新渲染組件。subscribe 函數會返回清除訂閲的函數。
  • getSnapshot:一個函數,返回組件需要的 store 中的數據快照。在 store 不變的情況下,重複調用 getSnapshot 必須返回同一個值。如果 store 改變,並且返回值也不同了(用 Object.is 比較),React 就會重新渲染組件。
  • 可選 getServerSnapshot:一個函數,返回 store 中數據的初始快照。它只會在服務端渲染時,以及在客户端進行服務端渲染內容的激活時被用到。快照在服務端與客户端之間必須相同,它通常是從服務端序列化並傳到客户端的。如果你忽略此參數,在服務端渲染這個組件會拋出一個錯誤。
返回值

該 store 的當前快照,可以在你的渲染邏輯中使用。

警告
  • getSnapshot 返回的 store 快照必須是不可變的。如果底層 store 有可變數據,要在數據改變時返回一個新的不可變快照。否則,返回上次緩存的快照。
  • 如果在重新渲染時傳入一個不同的 subscribe 函數,React 會用新傳入的 subscribe 函數重新訂閲該 store。你可以通過在組件外聲明 subscribe 來避免。
  • 如果在 非阻塞 Transition 更新 過程中更改了 store,React 將會回退並將該更新視為阻塞更新。具體來説,在每次 Transition 更新時,React 將在將更改應用到 DOM 之前第二次調用 getSnapshot。如果它返回的值與最初調用時不同,React 將重新從頭開始進行更新,這次將其作為阻塞更新應用,以確保屏幕上的每個組件都反映 store 的相同版本。
  • 不建議根據 useSyncExternalStore 返回的 store 值暫停渲染。原因是對外部 store 的變更無法 被標記為非阻塞 Transition 更新,因此它們會觸發最近的 Suspense,用加載旋轉器替換已經呈現在屏幕上的內容,通常會導致較差的用户體驗。
18、useTransition

useTransition 是一個讓你可以在後台渲染部分 UI 的 React Hook。

const [isPending, startTransition] = useTransition()
參考

useTransition()

在組件頂層調用 useTransition,將某些狀態更新標記為 transition。

import { useTransition } from 'react';

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  // ……
}
參數

useTransition 不需要任何參數。

返回值

useTransition 返回一個由兩個元素組成的數組:

  1. isPending,告訴你是否存在待處理的 transition。
  2. startTransition,你可以使用此方法將更新標記為 transition。
startTransition 函數

useTransition 返回的 startTransition 函數允許你將更新標記為 Transition。

參數
  • action:通過調用一個或多個 set 來更新某些狀態的函數。React 會立即調用 action(無需參數),並將 action 函數調用期間同步調度的所有狀態更新標記為 Transition。在 action 中通過 await 等待的異步調用會被包含在 Transition 中,但目前需要在 await 之後將任何 set 函數再次包裹在 startTransition 中(參見疑難解答)。標記為 Transition 的狀態更新將具備非阻塞特性,並且不會顯示不必要的加載指示。
返回值

startTransition 不返回任何值。

注意
  • useTransition 是一個 Hook,因此只能在組件或自定義 Hook 內部調用。如果需要在其他地方啓動 transition(例如從數據庫),請調用獨立的 startTransition 函數。
  • 只有在可以訪問該狀態的 set 函數時,才能將其對應的狀態更新包裝為 transition。如果你想啓用 Transition 以響應某個 prop 或自定義 Hook 值,請嘗試使用 useDeferredValue。
  • 傳遞給 startTransition 的函數會被立即執行,並將在其執行期間發生的所有狀態更新標記為 transition。如果你嘗試在 setTimeout 中執行狀態更新,它們將不會被標記為 transition。
  • 你必須將任意異步請求之後的狀態更新用 startTransition 包裹,以將其標記為 Transition 更新。這是一個已知限制,我們將在未來版本中修復(參見疑難解答)。
  • startTransition 函數具有穩定的標識,所以你經常會看到 Effect 的依賴數組中會省略它,即使包含它也不會導致 Effect 重新觸發。如果 linter 允許你省略依賴項並且沒有報錯,那麼你就可以安全地省略它。
  • 標記為 Transition 的狀態更新將被其他狀態更新打斷。例如在 Transition 中更新圖表組件,並在圖表組件仍在重新渲染時繼續在輸入框中輸入,React 將首先處理輸入框的更新,之後再重新啓動對圖表組件的渲染工作。
  • Transition 更新不能用於控制文本輸入。
  • 目前,React 會批處理多個同時進行的 transition。這是一個限制,可能會在未來版本中刪除。