😽

useTransitionとuseActionStateについて

2024/09/29に公開

はじめに

React19 でかなり使われるであろう useTransition と
useActionState についてなるべく初学者でも
わかりやすいようにまとめてみました。

useTransition は React18 からあるのですが
React19 から機能が強化されました!

useActionState は React19 から使える API になります!

Canary 版をにしないと使用できないで注意してください。

そもそも useTransition とは

react 公式では useTransition とは UI をブロックせずに state を
更新するための React フックですと書かれております。
どういうことかと説明すると、
本来であれば重いコンポーネントに接続した際
他のことが出来なくなると思いますが,
useTransition を用いることにより、
重いコンポーネントを読み込み中に state を更新することが出来ます。

useTransition使い方

Contact(重い)は重い処理の為表示するのに時間がかかるのですが
useTransition のおかげで読み込み中に Top に戻れるようになっています。

これはどういう仕組みかというと
視覚的に見ている react ツリーとはまた別に
裏側でもう一つの react ツリーが data を表に表示できるように準備してくれています。
その為、読み込み中に裏側の react ツリーを読み込むことにより
Top に戻れるということです。

useTransition はisPendingstartTransitionを返します。

index.tsx
import {useTransition} from "react";

export default function Page() {
  const [isPending, startTransition] = useTransition();

  //...
}

isPending は保留中かどうかを示します。

startTransition はstartTransition()の中で囲んだ処理は
優先度が低いものですよと位置付けることが出来ます。

index.tsx
<button
  onClick={() => {
    startTransition(() => {
       // この中の処理が優先度低くなる
    });
  }}
>
  送信
</button>

優先度が低いものですよと位置付けることにより
新しいアクションが発生した際に、
そこに割り込んで処理を行うことができる仕組みになっています。

ここまでが React18 での useTransition の使い方でした。

React19 での useTransition

React19 からは useTransition に非同期処理を行えるようになりました。
useTransition を上手く使うと
このようなことができます。

index.tsx
  const [word, setWord] = useStat(null);
  const [isPending, setIsPending] = useState(false);

  const handleClick = async () => {
    setWord(null); //現在の言葉をリセット
    setIsPending(true); //処理中と判定
    const word = await getRandomWord(); //ランダムに言葉を取得するAPI関数
    setWord(word); // 言葉を更新
    setIsPending(false);  //処理中おわり
  };

上記はクリックした際にランダムに言葉を表示する関数です。
useState で word を更新、
isPending は処理中かどうかを判定しています。
getRandomWord 関数の中で API を取得していると思ってください。

上記の関数で useTransition を用いると
このようにスッキリさせることができます。

index.tsx
  const [word, setWord] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleClick =  () => {
    setWord(null); //現在の言葉をリセット
    startTransition(async () => {  // この中の処理が優先度低くなる
     const word = await getRandomWord(); //ランダムに言葉を取得するAPI関数
     setWord(word) // 言葉を更新
    })
  };

同じ挙動になるのですが useTransition で
ローディングが自動で管理されるので
isPending を書く必要がなくなり
スッキリしたんじゃないでしょうか!
以上が React19 での useTransition になります。

useActionState とは

react 公式ではフォームアクションの結果に基づいて
state を更新するためのフックですと記載されています。
React18 では useFormState という名前の役割をしていました。

React19 での useActionState

挙動は useReducer と似ていて
現在の state と action を受け取って
新しい state を返すようになっています。

useReducer の場合

(prevState, action) => newState;

となるのですが
決定的な違いとして useActionState は 非同期関数 を使えるようになります。

async (prevState, action) => newState;

となります。コードを用いて説明します。

index.tsx
  const [Word, setWord] = useState([]);
  const [isPending, setIsPending] = useState(false);
  const handleSubmit = async (e) => {
    e.preventDefault(); //画面が更新されてしまうブラウザの処理を防ぐことができます。
    setWord([]); //検索したワードを空にする
    const query = e.currentTarget.query.value; //検索したいワードを取得
    if (!query) { // 検索ワードがなければそのまま返す
      return;
    }
    setIsPending(true); //ローディング開始
    const word = await searchWord(query); //このコードから検索用APIを取得
    setWord(word); //言葉の更新
    setIsPending(false); //ローディング終わり
  }
    return (
      <form onSubmit={handleSubmit}>

今度は検索ボタンを押した際に言葉を検索する関数です。
form 要素に onSubmit として関数を渡してあげます、
こちらの処理を useActionState を用いると
このようになります。

index.tsx
const [words, formAction, isPending] = useActionState(
  async (_prev, formData) => {
    const query = formData.get("query"); //検索ワードを取得
    if (!query) {  // 検索ワードがなければそのまま返す
      return [];
    }
    const words = await searchWords(query); //このコードから検索用APIを取得
    return words; // 言葉の更新
  },
  [], //初期値
);
return (
  <form action={formAction}>

だいぶスッキリしたと思います。

大きな点として、useState,isPending を使わないのと
form にonSubmitではなくactionを使います

細かい補足として色々挙げますと
今回は action 名に formAction と名付けていますが、
なんでもいいです

_prev の部分ですが、、今回は使っていないので_を頭に置いています。

isPending の部分は

{isPending ? (
  <span>ローディング中...</span>
) : (
    {words.map((word) => (
      <div key={word.title}>
        <h2>{word.title}</h2>
      </div>
    ))}
  </div>
)}

みたいな三項演算子を用いることで処理中の動作を行うことができ、
わざわざsetIsPending(true);みたいなコードを
書かなくても処理中かどうか自動で行なってくれます。

e.preventDefault();
setWord([]);

上記の画面が更新されてしまうブラウザを防ぐ処理と
検索した際に取得した値を空にする処理は
react19 でデフォルトで備わっているため
書く必要がなくなりました。

あと良い点として form で action を使っているので
プログレッシブエンハンスメントとして Javascript を使ってない
ブラウザでも使えるようになります。

async (_prev, formData) => {

と今回はしていますが、
第 3 引数に permalink を指定することができます。

これは API のリンクを指定した際は
Submit 後に画面を更新させて表示することができます。

useActionState はすごいぞ

useTransitionの際に使っていたコードを
useActionSrateを用いるとさらに短く書くことができます。

const [word, setWord] = useState(null);
const [isPending, startTransition] = useTransition();

const handleClick = () => {
  setWord(null);
  startTransition(async () => {
    const word = await getRandomWord();
    setWord(word);
  });
};

こちらのコードを useActionSrate を使うと...

const [word, formAction, isPending] = useActionState(async () => {
  const word = await getRandomWord(); //ランダムに言葉を取得する関数
  return word; // 言葉の更新
}, null); //第2引数に初期値を指定

return (
  <form action={formAction}>
    <button type="submit" disabled={isPending}>
      言葉を表示
    </button>
  </form>
);

これだけで済むようになります。
useState が書かなくて良くなるので
さらにスッキリしましたね。

ただし、注意点としていくつかあります。
form 要素で囲って action を指定しなくてはならないのと
button の type を button ではなく submit にする。

まとめ

useTransition と useActionState の良さ
伝わりましたでしょうか!
ざっくりまとめると
ローディングの部分や state の部分を
react 側に管理を任せることができるようになります!

間違っている部分ございましたら、ご指摘いただけると幸いです。

最後までお読みいただきありがとうございました。

Discussion