Reactのフックをループの中で使えない理由と解決策

React

こんにちは、白々さじきです。

今回は、Reactのフックを `.map()` などのループの中で使えない理由と、その対処法についてまとめます。

実際に実装していて「なんでここでフックを呼んだらエラーになるの?」と詰まったので、備忘録も兼ねて書きます。(私の学習のために生成AIに作らせた文章を一部加筆修正しました。)

結論

Reactのフックは「呼んだ順番」で状態を管理しています。ループの中で呼ぶと呼ばれる回数が変わるため、エラーになります。

対処法は、**フックを使いたい処理を1つのコンポーネントに切り出す**ことです。

Reactのフックとは

`use` で始まる関数のことをフックと呼びます。状態管理や副作用などの仕組みをコンポーネントの中で使えるようにしたものです。

今回は、最近私が勉強した一部のReactフックに関して簡単に用途を記載します。

フック 用途
useState状態(データ)を管理する
useEffectデータ取得・タイマーなどの副作用を実行する
useRefDOM要素への参照を保持する

フックの2つのルール

Reactには「フックのルール(Rules of Hooks)」という制約があります。

ルール1: コンポーネントのトップレベルでのみ呼ぶ

ループ・条件分岐・ネストした関数の中では呼べません。

ルール2: Reactのコンポーネントまたはカスタムフックの中でのみ呼ぶ

通常のJavaScript関数の中では呼べません。

なぜループの中で呼べないのか

Reactはフックを「呼んだ順番」で追跡しています。

たとえば、最初のレンダリングでこの順番でフックが呼ばれたとします。

1回目: useState(名前)

2回目: useState(年齢)

3回目: useEffect(データ取得)

次のレンダリングでも同じ順番で呼ばれることを、Reactは前提にしています。

ループの中でフックを呼ぶと、配列の要素数によって呼ばれる回数が変わります。

データが3件のとき → フックが3回呼ばれる

データが5件のとき → フックが5回呼ばれる

呼ばれる回数が変わると、Reactはどの状態がどのフックに対応するのか分からなくなります。これを防ぐためにルールとして禁止されています。

NG例とOK例

リストの各アイテムに「開閉できるアコーディオン」を実装する例で見てみます。

NG例: .map() の中でフックを呼んでいる

type Item = { id: string; name: string; detail: string };

// NG: .map() の中でフックを呼んでいる

const ItemList = ({ items }: { items: Item[] }) => {

  return (

    <ul>

      {items.map((item) => {

        const [isOpen, setIsOpen] = useState(false); // エラー

        return (

          <li key={item.id} onClick={() => setIsOpen(!isOpen)}>

            {item.name}

            {isOpen && <p>{item.detail}</p>}

          </li>

        );

      })}

    </ul>

  );

};

OK例:アイテム1つ分をコンポーネントに切り出す

// OK: アイテム1つ分をコンポーネントに切り出す

const AccordionItem = ({ item }: { item: Item }) => {

  const [isOpen, setIsOpen] = useState(false); // コンポーネントのトップレベルなのでOK

  return (

    <li onClick={() => setIsOpen(!isOpen)}>

      {item.name}

      {isOpen && <p>{item.detail}</p>}

    </li>

  );

};

// 親コンポーネントで .map() する

const ItemList = ({ items }: { items: Item[] }) => {

  return (

    <ul>

      {items.map((item) => (

        <AccordionItem key={item.id} item={item} />

      ))}

    </ul>

  );

};

コンポーネントとして切り出すと、それぞれのコンポーネントが「自分のトップレベル」でフックを呼べるようになります。

まとめ

・Reactのフックは「呼んだ順番」で管理されているため、ループの中では呼べない

・`.map()` の中でフックを使いたいときは、コンポーネントに切り出す

・切り出したコンポーネントのトップレベルでフックを呼べばOK

最初はエラーの意味がよく分からなかったのですが、「順番で管理している」という仕組みを知ってから理解できました。同じところで詰まった方の参考になれば幸いです。

参考リンク

フックのルール(Rules of Hooks)

各フックの詳細な説明

サポートのお願い

下記リンクからお買い物いただけると、ブログ運営のための費用が増え、有料サービスを利用した記事作成が可能になります。ご協力よろしくお願いします!



コメント

タイトルとURLをコピーしました