【図解解説】これ1本12分でReact Hooks 全20種を理解できる教科書

![react-hooks-allｒ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/8807e305-6554-455a-84d1-acf4878d1668.png)


# はじめに

こんにちは、[@Sicut_study](https://x.com/Sicut_study)です。

Reactを勉強するとまず最初に勉強するのがuseStateなどのHooksだったと思います。
`useState`や`useEffect`などは利用する場面が多く慣れている方も多いと思いますが、その他のHooksはどうでしょうか？そもそも名前すら知らないというHooksがたくさんあるかと思います。

その中には利用することでパフォーマンスを向上させたり、ステートを簡単に扱えるようになるものなど便利なものがたくさん用意されています。

React19の登場で`useActionState`や`useOptimistic`など絶対に覚えて活用していきたい重要なHooksも登場しております。

この記事ではそんなReactで用意されている全てのHooksを12分で読める内容にして紹介していきます。
最後まで読めばどのタイミングでどのHooksを選択すればよいかわかるようになるので、よりReactを使いこなせるようになり差別化できるかと思います。

# 動画教材も用意しています
こちらの教材にはより詳しく解説した動画もセットでご用意しています。
テキスト教材でわからない細かい箇所があれば動画も活用ください

<iframe width="560" height="315" src="https://www.youtube.com/embed/rMRWkeXN29g?si=Gd6xlrEs6nE-FKL7" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# 対象者
- Reactを勉強し始めた方
- もっとReactを深く理解したい方
- 新しいHooksを理解したい方
- 短時間で学びたい方

# React Hooksの全体像

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/d2e15ec6-bdea-435d-bc83-85d79ff72384.png)

まずはReact Hooksの全体像について理解していきましょう。
現在Reactでは19個のHooksと1個のAPIが用意されています。

React19になって`use`というAPI(関数)も追加されました。

この記事ではこれまでのHooksの使用頻度が高いものから順番に紹介していき、最後にReact19で追加されてものを紹介していきます。

# 01. useState

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/7f123ab6-8862-4516-9b0f-8717ce9d0310.png)

useStateを使って状態管理をすると、ステートが変更される度に再レンダリングが行われて画面が更新します。

```tsx
const UseState = () => {
  const [text, setText] = useState('');
  
  return (
    <div className="p-4">
      <input 
        type="text" 
        value={text} 
        onChange={(e) => setText(e.target.value)}
        className="border border-gray-300 rounded px-2 py-1 mb-2"
      />
      <div>入力されている値: {text}</div>
    </div>
  );
}
```

たとえばこのコンポーネントの場合、インプットフォームの値が更新されるとステートの更新関数に入力された内容が渡り、ステートが更新されます。

```ts
      <input 
        type="text" 
        value={text} 
        // e.target.valueは入力された値
        onChange={(e) => setText(e.target.value)}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/e5bf8881-79ef-4637-9928-416cd1f00d28.png)
　　　

useStateを使わない場合は画面が更新されません。
この例は変数の値を直接変えていますが、画面には反映されていないです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/14ea412c-afc2-443b-9ba1-38ff5d88375e.png)
   
   

ショッピングカートの合計金額の計算などにも利用できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/a5dde648-5edf-414c-9b01-f88917bab09a.png)


# 02. useEffect

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/cfc80266-a78a-4aac-9e24-4dc5db7c9946.png)

useEffectのよくある勘違いがあります。
<font color="red">**useEffectでデータ取得はしないほうがいいです**</font>

useEffectでデータを取得するような以下のコードはパフォーマンスの観点からよくないです。

```tsx
  useEffect(() => {
    // ユーザーデータの取得
    const fetchUserData = async () => {
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();
      setUserData(data);
    };
```

理由としては大きく2つあります。

1. ウォーターフォール問題

親コンポーネントのデータフェッチが完了するまで、子コンポーネントはデータフェッチが行われないのでパフォーマンスが落ちてしまう

2. レースコンディションの問題

複数のリクエストが同時に発生した場合、古いリクエスト(遅い)の
結果が新しいリクエスト(速い)の結果を上書きしてしまう可能性がある。

```tsx
function SearchComponent() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);

  useEffect(() => {
    // 検索を実行する
    fetch(`/api/search?q=${query}`)
      .then(res => res.json())
      .then(data => setResults(data));
  }, [query]);

  return (
    <div>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="検索..."
      />
      <ul>
        {results.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}
```

このような商品を検索するシステムがあるとします。

```ts
    fetch(`/api/search?q=${query}`)
      .then(res => res.json())
      .then(data => setResults(data));
```

このAPIの返答速度が

```
検索語：りんご
応答時間：3秒（重い検索）

検索語：りんごジュース
応答時間：1秒（軽い検索）
```

としたときに「りんご」と検索したあとすぐに「りんごジュース」と検索すると画面には「りんご」の検索が反映されてしまいま

そこでデータ取得をするなら`React Query`や`SWR`などを利用しましょう。
これらのライブラリを使うことで「キャッシュ」「自動再取得」「エラーハンドリング」などを簡単に実現できます。

# 03. useReducer

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/a0468d98-ce94-43c0-b7ce-d6f6a53b93bf.png)

アクションという概念を理解するためにカウンターの例を載せます

```tsx
import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    default:
      return state;
  }
}

function Counter() {
  const [count, dispatch] = useReducer(reducer, 0);
  
  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>
        増やす
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>
        減らす
      </button>
    </div>
  );
}
```

ボタンにタイプを指定することでreducerの中でどのボタンが押されたかを判定してステートを更新しています。
それぞれのボタンに`onClickPlus1`や`onClickminus1`のような関数を用意して指定する必要がなく、1つの関数を使いまわすことができています。

`useReducer`を使うと良いタイミングは大きく2つあります。

1. 複数の状態が関連している場合

```tsx
// useStateだと別々に管理
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [isValid, setIsValid] = useState(false)

// useReducerなら一つのオブジェクトで管理できる
const [formState, dispatch] = useReducer(reducer, {
  name: '',
  email: '',
  isValid: false
})
```

このような場合に利用することでそれぞれの項目ごとにuseStateを用意しなくて良くなります。
ただこの場合はReact Hook Formなどを利用するほうがよいでしょう。

2.状態の更新ロジックが複雑な場合

- 条件分岐が多い
- 複数の状態を同時に更新する必要がある
- バリデーションなど、追加の処理が必要

このようなときにuseReducerを使うことでuseStateより扱いやすくなります。

# 04. useContext

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/ee8b4de0-eff8-40af-ba66-e7cb94f2ec74.png)

useContextを利用することでグローバルに値を持つことができるので好きなコンポーネントから値を直接使うことが可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/b3255579-572e-4569-b3a2-4dc5067708b0.png)

useContextを利用しない場合「Propsのバケツリレー」を行わないといけないです。
例えば赤色の親コンポーネントから末端のコンポーネントに値を受け渡す場合、関係ないコンポーネントに渡していかないといけないので大変です。

# 05. useRef

**DOM参照や再レンダリングせずに値を保持できるフックです。**
利用シーンには「フォームの入力要素への参照」「スクロール位置の制御」「タイマーIDの保持」などがあります。

## 1. DOMの直接アクセス

```tsx
const MyComponent = () => {
  const inputRef = useRef(null);
  
  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>フォーカスを当てる</button>
    </div>
  );
};
```

useRefで目印を用意してインプットフォームに紐付けをします。

```tsx
  const inputRef = useRef(null);
```

```tsx
      <input ref={inputRef} type="text" />
```

ボタンをクリックしたら目印(inputRef)を使ってinput要素を取得しフォーカス直接実行します。

```tsx
  const focusInput = () => {
    inputRef.current.focus();
  };
```

## 2. 再レンダリングせずに保持

refを使うことで再レンダリングせずに値を保持することも可能です。

```tsx
const MyComponent = () => {
  const countRef = useRef(0);
  
  const handleClick = () => {
    countRef.current = countRef.current + 1;
    console.log('クリック回数:', countRef.current);
  };

  return <button onClick={handleClick}>クリック</button>;
};
```

useStateを使うと値が更新する度に再レンダリングが発生します。
しかし、この例のように画面に関係ない値を保持しておくケースではuseRefを使うことで再レンダリングなしにカウントの値を保持しておくことができます。

# 06. useMemo

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/01a448bf-8f2d-428b-a48e-89dea8bd7d41.png)

<font color="red">**React19以降を利用している場合は不要**</font>

useMemoを使用しないと以下のケースでパフォーマンスが悪くなってしまいます。

```tsx
// useMemoを使わない場合
function MovieList({ movies, searchTerm }) {
  const filteredMovies = movies.filter(movie => 
    movie.title.includes(searchTerm)
  );

  return (
    <ul>
      {filteredMovies.map(movie => (
        <li key={movie.id}>{movie.title}</li>
      ))}
    </ul>
  );
}
```

このコンポーネントを使っている親コンポーネントがあるとします。
その親コンポーネントの何かしらのステートが更新されると**moviesが変更されていなくても**再レンダリングが行われてしまいます。

# 07. useCallback

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/f65b2b9e-ff50-4b4a-9574-7ca0a3de0936.png)

<font color="red">**React19以降を利用している場合は不要**</font>

useMemoが値のメモ化をするのに対して、関数のメモ化をするのがuseCallbackです。
使うシーンは子コンポーネントがメモ化されているときです。

```tsx
// 親コンポーネント
function ParentComponent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log("クリック");
  }, []); 

  return (
    <>
      <ChildComponent onClick={handleClick} />
      <div>カウント: {count}</div>
    </>
  );
}
```

```tsx
const ChildComponent = React.memo(({ onClick }) => {
  console.log("子コンポーネントが再レンダリング");
  return <button onClick={onClick}>クリック</button>;
});
```

この場合、親コンポーネントのcountが更新されても子コンポーネントの再レンダリングは行われません。ただしメモ化していないコンポーネントに渡した場合は意味がないです。

# 08. useLayoutEffect

**DOMの変更を同期的に処理するためのフックです。**

イメージしやすいようにuseEffectではうまくいかないコードをみてみましょう。

```tsx
function FlickerComponent() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    setPosition({ x: window.innerWidth - 100, y: window.innerHeight - 100 });
  }, []);

  return (
    <div
      style={{
        position: 'absolute',
        left: `${position.x}px`,
        top: `${position.y}px`
      }}
    >
      Hello World
    </div>
  );
}
```

この例ではHello WorldのDivを初期表示で画面の外(みえないように)しようとしています。
しかしuseEffectを利用すると一瞬Hello Worldが画面に表示されてしまいます。

そこで`useLayoutEffect`を利用することができます。

```tsx
function NoFlickerComponent() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useLayoutEffect(() => {
    setPosition({ x: window.innerWidth - 100, y: window.innerHeight - 100 });
  }, []);

  return (
    <div
      style={{
        position: 'absolute',
        left: `${position.x}px`,
        top: `${position.y}px`
      }}
    >
      Hello World
    </div>
  );
}
```

それぞれの画面の更新タイミングがことなっているため、useLayoutEffectのほうが適しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/eefab7a7-292f-4224-89b7-bea0ea113e7f.png)

# 09. useTransition

**優先度の低い状態更新を後回しにできるフックです。**
「ページネーションの切り替え」や「検索機能」「タブの切り替え」に利用できます。

```tsx
function Search() {
  const [searchTerm, setSearchTerm] = useState('');
  const [isPending, startTransition] = useTransition();

  const handleSearch = (e) => {
    // 入力値の更新は即時に
    setSearchTerm(e.target.value);

    // 検索結果の更新は後回し
    startTransition(() => {
      // 大量のデータを検索する処理(時間がかかる)
    });
  };

  return (
    <>
      <input value={searchTerm} onChange={handleSearch} />
      {isPending && <div>更新中...</div>}
    </>
  );
}
```

処理に時間がかかる検索などは、検索している途中ユーザーが操作しても画面が反応しなくなってしまいユーザー体験が落ちてしまいます。そこで検索処理の優先度を`useTransition`で落とすことでUXを向上できます。


トランジション関数と処理中かを表すisPending(true/false)を用意して

```tsx
  const [isPending, startTransition] = useTransition();
```

時間のかかる処理を`startTransition`の中で呼び出すようにします。

```tsx
    startTransition(() => {
      // 大量のデータを検索する処理(時間がかかる)
    });
```

処理が終わるまでisPendingは`true`となるので利用してローディングをだすことも可能です。

```tsx
{isPending && <div>更新中...</div>}
```

# 10. useDeferredValue

**値の更新を遅延させることができるフックです。**
useTransitionは関数自体を遅延していますが、useDeferredValueは値を遅延できます。

このHooksの利用シーンは「利用シーン : サードパーティのコンポーネントを使うなど、コードを直接制御できない場合」にあります。

```tsx
import React, { useState, useDeferredValue } from 'react';
import ExpensiveThirdPartyList from 'some-third-party-library';
function SearchableList() {
  const [searchQuery, setSearchQuery] = useState('');
  const deferredQuery = useDeferredValue(searchQuery);

  const handleSearch = (e) => {
    setSearchQuery(e.target.value);
  };

  return (
    <div>
      <input 
        type="text" 
        value={searchQuery}
        onChange={handleSearch} 
        placeholder="検索..."
      />

      {/* サードパーティのコンポーネントに遅延された値を渡す */}
      <ExpensiveThirdPartyList 
        searchText={deferredQuery} 
        // その他のprops...
      />
    </div>
  );
}

export default SearchableList;
```

このように処理を直接制御できないサードパーティのコンポーネントでも使用できます。
ExpensiveThirdPartyListの再レンダリングに時間がかかったとしても、遅延しているためUXがよくなります。

# 11. useId

**一意のIDを生成するためのフックです。**
主にアクセシビリティやSSRの安全性のために利用します。

## 1. ラベルとフォームの紐付け (アクセシビリティ)

```tsx
function NameInput() {
  const id = useId();

  return (
    <div>
      <label htmlFor={id}>名前：</label>
      <input id={id} type="text" />
    </div>
  );
}
```

使い方は`useId`を呼び出してidを取得するだけです。
インプットフォームとラベルを紐付けるのに利用しています。

## 2. 複数IDが必要な場合 (SSRの安全性)

```tsx
function Form() {
  const id = useId();

  return (
    <form>
      <div>
        <label htmlFor={`${id}-name`}>名前：</label>
        <input id={`${id}-name`} type="text" />
      </div>
      <div>
        <label htmlFor={`${id}-email`}>メール：</label>
        <input id={`${id}-email`} type="email" />
      </div>
    </form>
  );
}
```

このようなときにランダムなIDを使うと、サーバーサイドレンダリング（SSR）で不整合が起きる可能性があります。(同じIDがつく可能性)

# 12. useSyncExternalStore

**外部ストア（Redux等）からデータを安全に読み取るためのフックです。**
利用することが滅多にないフックで、独自の状態管理ライブラリなどを作る際に利用します。

```tsx
// カスタムストアを作成する例

const createStore = (initialState) => {
  let state = initialState;
  const listeners = new Set();

  return {
    subscribe(listener) {
      listeners.add(listener);
      return () => listeners.delete(listener);
    },
    getState() {
      return state;
    },
    setState(newState) {
      state = newState;
      listeners.forEach(listener => listener());
    }
  };
};
```

Reduxなどを利用しないとなかなか使う機会はすくないです。
ただ以下のようにブラウザAPIを使って画面サイズを取得することなどには利用できるかもしれません。

```tsx
// ブラウザAPIと連携する例
function WindowSize() {
  const size = useSyncExternalStore(
    // ウィンドウサイズの変更を監視
    (onStoreChange) => {
      window.addEventListener('resize', onStoreChange);
      return () => window.removeEventListener('resize', onStoreChange);
    },
    // 現在のサイズを取得
    () => ({
      width: window.innerWidth,
      height: window.innerHeight
    })
  );

  return <div>Window size: {size.width} x {size.height}</div>;
}
```

# 13. useDebugValue

**React DevToolsでデバックをしやすくするフックです。**
React DevToolsを使っていない場合は使用しません。

```tsx
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // フェッチの処理が入る
  }, [url]);

  useDebugValue({
    url,
    state: loading ? 'loading' : error ? 'error' : 'success'
  });

  return { data, loading, error };
}
```

useDebugValueに値をいれることでReact DevToolsでコンソール表示が可能です。

# 14. useImperativeHandle

**子コンポーネントのrefで公開する値をカスタマイズするためのフックです。**

```tsx
// 親コンポーネント
function Parent() {
  const inputRef = useRef();

  const handleClick = () => {
    // 子コンポーネントのメソッドを呼び出し
    inputRef.current.focus();
    inputRef.current.clear();
  };

  return (
    <div>
      <CustomInput ref={inputRef} />
      <button onClick={handleClick}>
        フォーカス＆クリア
      </button>
    </div>
  );
}
```

```tsx
import { forwardRef, useImperativeHandle } from 'react';

// forwardRefと組み合わせて使う
const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  // 親コンポーネントに公開するメソッドを定義
  useImperativeHandle(ref, () => ({
    // カスタムメソッド
    focus: () => {
      inputRef.current.focus();
    },
    // 入力値をクリア
    clear: () => {
      inputRef.current.value = '';
    }
  }));

  return <input ref={inputRef} {...props} />;
});
```

親のボタンをクリックすることで子コンポーネントのインプットフォームを操作することができています。
ちなみに`forwardRef`というのを子コンポーネントで利用しているのは、React19以前は`ref`をPropsで渡すことができなかったからです。

しかし、React19になってrefを渡せるようになったので19以降を利用している場合は通常のPropsと同じように渡すだけで問題ありません。

# 15. useInsertionEffectReact

**CSS-in-JSライブラリの実装者向けの特殊なフックです。**

DOMの変更前に実行され、レイアウトの計算前にスタイルを挿入できます。
useLayoutEffectやuseEffectより先に実行されます。

```tsx
import { useInsertionEffect } from 'react';

function useCssInJs(rule) {
  useInsertionEffect(() => {
    // DOMへのスタイル挿入
    const style = document.createElement('style');
    style.textContent = rule;
    document.head.appendChild(style);

    return () => {
      // クリーンアップ
      document.head.removeChild(style);
    };
  }, [rule]);
}
```

# 16. useActionState

**非同期処理をしたあとの結果でステート更新ができるフックです。**

useTransition + useReducerのような機能で、非同期処理を待っている間はisPendingを利用してローディング画面などをだすことも可能です。そのあと処理が終わったらステートが更新されるので、画面の更新(再レンダリング)が行われます。

```tsx
const initialPosts = [];

const [posts, getPosts, isPending] = useActionState(
 async (currentPosts, payload) => {
   const response = await fetch('https://api.example.com/posts');
   const newPosts = await response.json();
   
   return newPosts;
 },
 initialPosts
);

const handleClick = () => {
 getPosts();
};

return (
 <div>
   <button onClick={handleClick} disabled={isPending}>
     {isPending ? '読み込み中...' : '投稿を取得'}
   </button>

   <ul>
     {posts.map(post => (
       <li key={post.id}>{post.title}</li>
     ))}
   </ul>
 </div>
);
```

まずはuseActionsStateを呼び出します。

```tsx
const [posts, getPosts, isPending] = useActionState(
 async (currentPosts, payload) => {
   const response = await fetch('https://api.example.com/posts');
   const newPosts = await response.json();
   
   return newPosts;
 },
 initialPosts
);
```

ここでpostsがステートの値、getPostsが引数の関数、isPendingがgetPostsが実行しているかを表すフラグ(true/false)になっています。第二引数のは初期値です。

```tsx
   <button onClick={handleClick} disabled={isPending}>
     {isPending ? '読み込み中...' : '投稿を取得'}
   </button>
```

ボタンがクリックされるとgetPostsが実行されてローディングが表示されます。
getPoostの中では新しいpostsを取得してリターンすることで、postsのステートを変更できます。

# 17. useOptimistic

**楽観的更新を行うためのフックです。**
楽観的更新とはユーザーのアクション結果を即座に画面に反映して、あとから実際の処理を行うことでUXを向上させるアプローチです。

```tsx
function LikeButton() {
 const [likes, setLikes] = useState(0);

 // optimisticLikesは楽観的な値、addOptimisticLikeは更新関数
 const [optimisticLikes, addOptimisticLike] = useOptimistic(
   likes,
   (state) => state + 1
 );

 const handleClick = async () => {
   // 楽観的に即座にカウントアップ
   addOptimisticLike();

   // 重い処理（3秒かかると仮定）
   await fetch('/api/like', { method: 'POST' });

   // 実際のデータで更新
   setLikes(likes + 1);
 };

 return (
   <button onClick={handleClick}>
     いいね！({optimisticLikes})
   </button>
 );
}
```

useOptimisticの楽観的なステート`optimisticLikes`と即座に更新するときに利用する関数を`addOptimisticLike`を用意します。

```tsx
 const [optimisticLikes, addOptimisticLike] = useOptimistic(
   likes,
   (state) => state + 1
 );
```

第一引数には実際のステート、第二引数には楽観的更新をするときに適応する関数をいれています。
ボタンがクリックしたら`addOptimistic`を実行してすぐに+1したものを画面に表示します。

```tsx
   addOptimisticLike();
```

そして重い処理が終わってから実際のステートを更新します。

```tsx
   // 重い処理（3秒かかると仮定）
   await fetch('/api/like', { method: 'POST' });

   // 実際のデータで更新
   setLikes(likes + 1);
```

もし途中で何かしらの例外が発生した場合楽観的なステートもすぐにもとのステートに戻り再レンダリングされます。

# 18. useFormState

**フォームのステート管理とサーバーアクションの結果を扱うためのフックです。**

```tsx
async function submitForm(prevState, formData) {
  // 入力内容の取得
  const name = formData.get('name');
  
  // バリデーションなど
  if (!name) {
    return { error: '名前を入力してください' };
  }

  // APIリクエストなど
  await saveToDatabase(name);
  
  return { success: true, message: '保存しました' };
}

function Form() {
  const [state, formAction] = useFormState(submitForm, {
    error: null,
    success: false,
    message: null
  });

  return (
    <form action={formAction}>
      <input type="text" name="name" />
      
      {/* エラー表示 */}
      {state.error && <p style={{color: 'red'}}>{state.error}</p>}
      
      {/* 成功メッセージ */}
      {state.success && <p>{state.message}</p>}
      
      <button type="submit">送信</button>
    </form>
  );
}
```

useFormStateを使うことでuseActionStateをフォームに特化して行うことが可能です。
流れはほとんど変わりません。

```tsx
async function submitForm(prevState, formData) {
  // 入力内容の取得
  const name = formData.get('name');
  
  // バリデーションなど
  if (!name) {
    return { error: '名前を入力してください' };
  }

  // APIリクエストなど
  await saveToDatabase(name);
  
  return { success: true, message: '保存しました' };
}

function Form() {
  const [state, formAction] = useFormState(submitForm, {
    error: null,
    success: false,
    message: null
  });
```

ステートを更新する関数を第一引数に渡して、初期値を第二引数に渡しています。

# 19. useFormStatus

**フォームの送信状態を管理するためのフックです。**

```tsx
'use client';
 
import { useFormStatus } from 'react-dom';
 
function SubmitButton() {
  const { pending } = useFormStatus();
 
  return (
    <button type="submit" disabled={pending}>
      {pending ? '送信中...' : '送信'}
    </button>
  );
}

function Form() {
  return (
    <form action={async () => { /* ... */ }}>
      <input type="text" name="name" />
      <SubmitButton />
    </form>
  );
}
```

useFormStatusは子コンポーネントで行う必要があり、親コンポーネントのform属性のサブミットの状況をisPendingのフラグ(true/false)で取得することができます。

# 20. use

**非同期データを扱うためのAPIです。**
useはReact Hooksではなく、APIと呼ばれています。

たとえば`useEffect`でデータを取得するとこのようにかけます。

```tsx
function Example1() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(res => res.json())
      .then(json => setData(json));
  }, []);

  return <div>{data?.message}</div>;
}
```

useを利用するとPromiseを直接利用することができます。

```tsx
function Example2() {
  const data = use(fetch('https://api.example.com/data').then(res => res.json()));
  return <div>{data.message}</div>;
}
```

useStateやuseEffectなどを利用せずにデータ取得ができます。
ただしローディング状態を把握することができないので**Suspense**と利用するの良いでしょう。

# おわりに

いかがでしたでしょうか？
Hooksの理解自体はそこまで難しくなかったかと思います。
React19のHooksはどれも実用的で今後利用していくことが多くなるかと思います。詳しく解説した動画を投稿しているのでよかったらみてみてください！

<iframe width="560" height="315" src="https://www.youtube.com/embed/_TUgbgIirus?si=GBmYytakvQ22uGEC" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

次回のハンズオンのレビュアーは[X](https://x.com/Sicut_study)にて募集します。

# JISOUのメンバー募集中！
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？
興味のある方は、ぜひホームページからお気軽にカウンセリングをお申し込みください！
▼▼▼

https://projisou.jp

# 図解ハンズオンたくさん投稿しています！

https://qiita.com/Sicut_study/items/c12faaf962646bee34f7

https://qiita.com/Sicut_study/items/a27fb53468e14216d6fb

https://qiita.com/Sicut_study/items/0318cc136c189b179b7f