React 19 筆記:hooks 篇

提醒:這篇筆記是我在讀完 React 19 Beta 後,參考官方文件對於各 hook 的說明整理出來的東西。這不是一篇把每個 hook 從頭講解到尾的文章,我只記錄自認重要的部分。如果你需要知道 React 19 預定帶來的新功能與每個 hook 的詳細解說,請務必閱讀官方文件。

useTransition

目的:避免繁重計算阻擋使用者與畫面互動。

使用範例:

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
  // ...
}

回傳內容:

搭配上方範例,我們在做的事情就是「將 setTab 標記為變遷」。在執行變遷時,畫面(UI)不會凍結。可以透過官方範例體驗一下「有無變遷」如何影響使用者與 App 互動的體驗。

而參數 isPending 則是能讓使用者不必透過 Suspense 來控制畫面,可參考官方範例來體驗局部 UI 變更的效果。

注意事項:

useDeferredValue

目的:在新結果計算完畢前,不更新畫面,而是顯示上一次的計算結果。與 useTransition 有點類似,這個 hook 的目的也是避免讓繁重的計算影響到使用者與 App 的互動體驗。

使用範例:

import { useState, useDeferredValue } from 'react';
import SlowList from './SlowList.js';

export default function App() {
  const [text, setText] = useState('');
  const deferredText = useDeferredValue(text);
  return (
    <>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <SlowList text={deferredText} />
    </>
  );
}

接收參數:

回傳內容:在第一次渲染時,會回傳使用者傳入的值(上述 value)。在重渲染時,React 會先回傳舊值,再回傳新值。搭配官方範例比較能體會這個「新舊值切換」到底在解決什麼問題。

注意事項:

useOptimistic

目的:在新結果計算完畢前,先用 optimisticState 更新畫面。

接收參數:

回傳結果

整理了一版改編自官方的使用範例,以下是此範例的詳細行為說明:

  1. App 的 sendMessage 功能接收 msg: string 作為參數,並以此值呼叫 api,最後再透過 setMessages 將 api 回傳的結果更新回本地狀態
  2. 使用者對 Thread 元件送出表單後,從表單取得 message 欄位的值(msg),並將此值傳給 addOptimistic(即 addOptimisticMsg)開始執行 action。接著清空表單內容,並呼叫 sendMessage(msg)
  3. 執行 addOptimisticMsg 時會發生的事情請參考範例第 14 到 21 行,我們將使用者輸入到表單的值(msg)加入 optimisticState(即 optimisticMsg)中,並設定 key sending 為 true
  4. 而因為我們使用 optimisticState(即 optimisticMsg)來渲染畫面,所以在 api 回傳實際結果前,會看到畫面上出現 (Sending...) 字樣
  5. 在 api 回傳結果後,回到步驟一,我們透過 setMessages 將 key sending 設定為 false,畫面上的 (Sending...) 消失

useActionState

目的:根據 form action 的結果來更新局部狀態。可參考官方範例感受其作用。

使用範例:

function ChangeName({ name, setName }) {
  const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      const error = await updateName(formData.get("name"));
      if (error) {
        return error;
      }
      redirect("/path");
      return null;
    },
    null,
  );

  return (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </form>
  );
}

接收參數:

回傳內容:

useFormStatus

目的:取得送出表單時的狀態。需注意此 hook 只能在表單的子元件中使用。

回傳內容可解構為以下四項資訊:

參考文件