【React】フック(hook)とは何か?カスタムフックとの違い|どこに記述するべきか?

react-prograshi(プロぐらし)-kv React.js
記事内に広告が含まれていることがあります。

「Reactを学び始めたけれど、フック(Hook)の意味がわからない…」「カスタムフックとの違いや、正しい記述場所がわからない…」と悩んでいませんか?

React開発の核となるフックは、コンポーネントで状態管理や副作用を扱うための強力な道具です。しかし、その独自のルールや「なぜ使うのか」という本質を理解していないと、思わぬバグに繋がることも少なくありません。

本記事では、フックの基本概念から、便利な自作ツールであるカスタムフックとの違い、そして記述のルールなどを解説しています。


フック(hook)とは何か?

Reactのフック(Hook)は、本来複雑なプログラムを書かないと使えなかったReactの内部システム(状態管理やメモリなど)にアクセスするきっかけとなる関数です

Reactが公式に用意したもので「use~」という形になってます。

Reactのバージョン16.8から導入され、今ではReact開発のスタンダードとなっています。


なぜフックと言うのか?

なぜフックと言うかは、Reactを起動したときの実際の動きを見るとわかりやすいです。

Next.jsの場合、page.tsxにコンポーネントを記述します。サーバーを起動すると、Reactのシステムがコンポーネントを実行します。

その実行中にuseStateなどのフックがあると、コンポーネントの外側にあるReactnの管理領域にアクセスしてデータを保存します。

このように、コンポーネントの外にあるReact本体の機能に接続(フック)するための関数なのでフックと呼ばれます。



Reactのフックの特徴とルール

フックの特徴
  • React公式が提供している
  • 必ず「use~」という形になっている
  • Reactの内部と直接つながっている
  • Reactのコンポーネント内だけで呼ぶ(普通のJavaScript関数の中では使えない)
  • 関数のトップレベルで呼ぶ
    if 文の中や、ループ(for 文)の中でフックを呼び出してはいけません。常にコンポーネントの関数の最初の方で呼びます。


Reactで使う主なフック

Reactに用意されている主なフックには以下があります。

Reactで使う主なフック
  1. useState
  2. useEffect
  3. useContext
  4. useRef
  5. useMemo

この中でもダントツでよく使うのが「useState」と「useEffect」です


useStateとは何か?

画面上で「変化するデータ」を扱うためのフックです。

Next.jsはデフォルトではサーバー側でHTMLを作成してブラウザに渡します(サーバーコンポーネント)。このため、ブラウザ側ではJavaScriptを使わず、画面上でデータを操作できません。

ブラウザで表示内容を操作したい場合はuseStateを使います。

import { useState } from 'react';

function Counter() {
  // countという変数と、それを更新するsetCountという関数を作る
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>クリック数: {count}</p>
      <button onClick={() => setCount(count + 1)}>増やす</button>
    </div>
  );
}


useEffectとは何か?

「画面が表示された直後」や「データが更新された時」など、特定のタイミングで何かをしたい時に使うフックです。

どのデータが更新されたら関数を実行するといった、監視対象を指定することができます。また、ページから離れるときに指定した関数(クリーンアップ関数)を実行することができます。

Firebaseなどの外部サーバーからデータを取得したり、タイマーを設定したりする時に使います。

import { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    // 1. タイマーを開始する
    const intervalId = setInterval(() => {
      setSeconds(s => s + 1);
      console.log("カウント中...");
    }, 1000);

    // 2. クリーンアップ関数を返す(後片付け)
    return () => {
      clearInterval(intervalId); // タイマーを止める
      console.log("タイマーを停止しました");
    };
  }, []); // 初回のみ実行

  return <div>経過時間: {seconds}秒</div>;
}


カスタムフックの意味とルール

カスタムフックとは何か?

カスタムフックとは、React公式のフックを組み合わせて自分で作った関数です。


カスタムフックの3つのルール

カスタムフックには次の3つのルールがあります。

カスタムフックの3つのルール
  1. 名前は必ず use で始める
    useWindowWidthuseAuth など。これによってReactがフックだと認識する
  2. 中で他のフックを使っている
    中で何もフックを使っていないなら、それはただの「普通の関数」です
  3. フックの基本ルールを守る
    if 文の中で呼ばない、といったフック自体のルールはカスタムフックでも同じです


なぜカスタムフックを作るのか?

Reactで開発していると「この useState と useEffect のセット、別の画面でも使ったな…」という場面が出てきます。

同じ処理を何度も書くと、コードが長くなり、修正も大変になります。

そこで、共通の処理を外に切り出して使い回しができるようにするのがカスタムフックを使う目的です。


カスタムフックの実例

「画面の横幅をリアルタイムで取得する」という処理を例を紹介します。

画面幅の保存には「useState」、 リサイズイベントの監視には「useEffect」を使います。

hooksディレクトリを作成する

使いまわすためのカスタムフックはルート直下のhooksディレクトリの中に作成します。

MEMO

必ずしもhooksの中に記述しなければいけないわけであはりません。page.tsxの上部に書いても動きます。

ただ、再利用する場合hooksディレクトリの中に各カスタムフックのファイルを作成することで管理しやすくなるので、この方法が推奨となります。

再利用しないカスタムフックはpage.tsxの上部に直接記述しても問題ありません。


カスタムフック名でファイルを作成する

今回はカスタムフックの名前を「useWindowWidth」とします。ファイル名も同じ名前(useWindowWidth.js)にします。

import { useState, useEffect } from 'react';

export function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return width;
}


カスタムフックを呼び出す

コンポーネントの中でカスタムフックを呼び出します。

例えば、MyComponentというコンポーネントの中で呼び出す場合は以下のようにします。

import { useWindowWidth } from '@/hooks/useWindowWidth';

function MyComponent() {
  const width = useWindowWidth(); // 使う側は useState を意識しなくてOK!

  return (
    <div>
      <p>現在の横幅は {width}px です</p>
      {width < 600 ? <p>モバイルサイズですね</p> : <p>PCサイズですね</p>}
    </div>
  );
}


Next.jsでuserStateとuseEffectを使うときの注意点

userStateとuseEffectはブラウザ側で動き、ブラウザに表示されている内容を変化させるものです。

コンポーネントはクライアントコンポーネントである必要があります。このため、上部に必ず以下の記述が必要となります。

"use client";
タイトルとURLをコピーしました