こんにちは、白々さじきです。
今回は、Reactのフックを `.map()` などのループの中で使えない理由と、その対処法についてまとめます。
実際に実装していて「なんでここでフックを呼んだらエラーになるの?」と詰まったので、備忘録も兼ねて書きます。(私の学習のために生成AIに作らせた文章を一部加筆修正しました。)
結論
Reactのフックは「呼んだ順番」で状態を管理しています。ループの中で呼ぶと呼ばれる回数が変わるため、エラーになります。
対処法は、**フックを使いたい処理を1つのコンポーネントに切り出す**ことです。
Reactのフックとは
`use` で始まる関数のことをフックと呼びます。状態管理や副作用などの仕組みをコンポーネントの中で使えるようにしたものです。
今回は、最近私が勉強した一部のReactフックに関して簡単に用途を記載します。
| フック | 用途 |
| useState | 状態(データ)を管理する |
| useEffect | データ取得・タイマーなどの副作用を実行する |
| useRef | DOM要素への参照を保持する |
フックの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
最初はエラーの意味がよく分からなかったのですが、「順番で管理している」という仕組みを知ってから理解できました。同じところで詰まった方の参考になれば幸いです。
参考リンク
サポートのお願い
下記リンクからお買い物いただけると、ブログ運営のための費用が増え、有料サービスを利用した記事作成が可能になります。ご協力よろしくお願いします!

コメント