React開発を爆速化!必須カスタムHooksトップ10【初心者向け徹底解説】

投稿日:
更新日:

1. カスタムHooksとは?React開発を効率化する魔法の杖

カスタムHooks(カスタムフック)とは、ReactのHooks APIを使って、独自のロジックを再利用可能な関数として定義したものです。簡単に言うと、コンポーネント間で共通の処理をまとめた便利な道具箱のようなものです。

1.1 なぜカスタムHooksを使うべきなのか?

カスタムHooksを使うことで、以下のメリットが得られます。

  • コードの再利用性向上: 同じロジックを複数のコンポーネントで共有できます。
  • コンポーネントの可読性向上: ロジックをHooksに分離することで、コンポーネントがシンプルになります。
  • テストの容易性向上: Hooksは独立した関数なので、単体テストが容易です。
  • 開発効率の向上: 共通の処理を毎回書く必要がなくなるため、開発速度が向上します。

1.2 カスタムHooksの基本

カスタムHooksは、useから始まる名前の関数として定義します。内部では、useStateuseEffectなどのReact Hooksを使用できます。

// useCounter.js
import { useState } from 'react';

function useCounter(initialValue = 0) {
  // useStateを使ってカウンターの状態を管理
  const [count, setCount] = useState(initialValue);

  // カウントを増やす関数
  const increment = () => {
    setCount(count + 1);
  };

  // カウントを減らす関数
  const decrement = () => {
    setCount(count - 1);
  };

  // カウントの状態と操作関数を返す
  return { count, increment, decrement };
}

export default useCounter;

この例では、useCounterというカスタムHookを定義しています。このHookは、カウンターの状態(count)と、カウントを増減させる関数(incrementdecrement)を返します。

2. 必須カスタムHooksトップ10

2.1 useFetch: データフェッチを簡単にするHook

useFetchは、APIからデータを取得する処理を簡単にするHookです。

// useFetch.js
import { useState, useEffect } from 'react';

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

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const json = await response.json();
        setData(json);
        setLoading(false);
      } catch (error) {
        setError(error);
        setLoading(false);
      }
    };

    fetchData();
  }, [url]); // urlが変わった時のみ実行

  return { data, loading, error };
}

export default useFetch;

使い方:

import useFetch from './useFetch';

function MyComponent() {
  const { data, loading, error } = useFetch('https://api.example.com/data');

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <ul>
      {data.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}</ul>
  );
}

useFetchを使うことで、データの取得、ローディング状態の管理、エラー処理をまとめて行うことができます。

2.2 useLocalStorage: ローカルストレージを簡単に扱うHook

useLocalStorageは、ブラウザのローカルストレージ(ブラウザにデータを保存する仕組み)を簡単に扱うためのHookです。

// useLocalStorage.js
import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  useEffect(() => {
    try {
      window.localStorage.setItem(key, JSON.stringify(storedValue));
    } catch (error) {
      console.error(error);
    }
  }, [key, storedValue]);

  return [storedValue, setStoredValue];
}

export default useLocalStorage;

使い方:

import useLocalStorage from './useLocalStorage';

function MyComponent() {
  const [name, setName] = useLocalStorage('name', '');

  return (
    <div>
      <label>
        Name:
        <input type="text" value={name} onChange={(e) => setName(e.target.value)} />
      </label>
      <p>Hello, {name}!</p>
    </div>
  );
}

useLocalStorageを使うことで、ローカルストレージへのデータの保存と取得が簡単に行えます。

2.3 useDebounce: 入力遅延処理を実現するHook

useDebounceは、入力欄への入力など、連続して発生するイベントの処理を遅延させるためのHookです。これにより、APIリクエストの回数を減らすなど、パフォーマンスを向上させることができます。

// useDebounce.js
import { useState, useEffect } from 'react';

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

export default useDebounce;

使い方:

import useDebounce from './useDebounce';

function MyComponent() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearchTerm = useDebounce(searchTerm, 500); // 500ms遅延

  useEffect(() => {
    // debouncedSearchTermが変更された時にAPIリクエストを実行
    console.log('APIリクエスト:', debouncedSearchTerm);
  }, [debouncedSearchTerm]);

  return (
    <div>
      <label>
        Search:
        <input type="text" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
      </label>
    </div>
  );
}

useDebounceを使うことで、入力が終わってから一定時間後に処理を実行することができます。

2.4 useUpdateEffect: 初回レンダリングをスキップするuseEffect

useEffectは、コンポーネントがマウントされた時と、依存配列の値が変更された時に実行されます。useUpdateEffectは、初回レンダリング時には実行されず、依存配列の値が変更された時のみ実行されるようにするためのHookです。

// useUpdateEffect.js
import { useEffect, useRef } from 'react';

function useUpdateEffect(callback, dependencies) {
  const firstRenderRef = useRef(true);

  useEffect(() => {
    if (firstRenderRef.current) {
      firstRenderRef.current = false;
      return;
    }
    return callback();
  }, dependencies);
}

export default useUpdateEffect;

使い方:

import useUpdateEffect from './useUpdateEffect';
import { useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  useUpdateEffect(() => {
    // countが変更された時に実行される
    console.log('Count changed:', count);
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

useUpdateEffectを使うことで、初回レンダリング時の不要な処理をスキップすることができます。

2.5 useWindowSize: ウィンドウサイズを監視するHook

useWindowSizeは、ウィンドウのサイズが変更された時に、そのサイズを取得するためのHookです。レスポンシブデザイン(画面サイズに応じてレイアウトを調整するデザイン)の実装に役立ちます。

// useWindowSize.js
import { useState, useEffect } from 'react';

function useWindowSize() {
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });

  useEffect(() => {
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return windowSize;
}

export default useWindowSize;

使い方:

import useWindowSize from './useWindowSize';

function MyComponent() {
  const windowSize = useWindowSize();

  return (
    <div>
      <p>Width: {windowSize.width}</p>
      <p>Height: {windowSize.height}</p>
    </div>
  );
}

useWindowSizeを使うことで、ウィンドウサイズに応じてコンポーネントの表示を切り替えることができます。

2.6 useToggle: 真偽値を切り替えるHook

useToggleは、真偽値(trueまたはfalse)を簡単に切り替えるためのHookです。

// useToggle.js
import { useState } from 'react';

function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);

  const toggle = () => {
    setValue(!value);
  };

  return [value, toggle];
}

export default useToggle;

使い方:

import useToggle from './useToggle';

function MyComponent() {
  const [isOn, toggleIsOn] = useToggle(false);

  return (
    <div>
      <p>Is On: {isOn ? 'Yes' : 'No'}</p>
      <button onClick={toggleIsOn}>Toggle</button>
    </div>
  );
}

useToggleを使うことで、ボタンのクリックなどで簡単に真偽値を切り替えることができます。

2.7 useTimeout: 一定時間後に処理を実行するHook

useTimeoutは、一定時間後に処理を実行するためのHookです。

// useTimeout.js
import { useEffect, useRef } from 'react';

function useTimeout(callback, delay) {
  const timeoutRef = useRef(null);
  const callbackRef = useRef(callback);

  // callbackが変更された場合に更新
  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);

  useEffect(() => {
    timeoutRef.current = setTimeout(() => {
      callbackRef.current();
    }, delay);

    return () => clearTimeout(timeoutRef.current);
  }, [delay]);
}

export default useTimeout;

使い方:

import useTimeout from './useTimeout';
import { useState } from 'react';

function MyComponent() {
  const [message, setMessage] = useState('Initial Message');

  useTimeout(() => {
    setMessage('Message after 3 seconds');
  }, 3000); // 3秒後に実行

  return (
    <div>
      <p>{message}</p>
    </div>
  );
}

useTimeoutを使うことで、一定時間後にメッセージを変更するなど、様々な処理を実行できます。

2.8 usePrevious: 前回の値を保持するHook

usePreviousは、コンポーネントがレンダリングされる前の値を保持するためのHookです。

// usePrevious.js
import { useRef, useEffect } from 'react';

function usePrevious(value) {
  const ref = useRef();

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}

export default usePrevious;

使い方:

import usePrevious from './usePrevious';
import { useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);
  const previousCount = usePrevious(count);

  return (
    <div>
      <p>Current Count: {count}</p>
      <p>Previous Count: {previousCount}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

usePreviousを使うことで、現在の値と前の値を比較するなど、状態の変化を追跡することができます。

2.9 useGeolocation: ユーザーの位置情報を取得するHook

useGeolocationは、ユーザーの現在地(緯度と経度)を取得するためのHookです。

// useGeolocation.js
import { useState, useEffect } from 'react';

function useGeolocation() {
  const [location, setLocation] = useState({
    latitude: null,
    longitude: null,
    error: null,
  });

  useEffect(() => {
    if (!navigator.geolocation) {
      setLocation({
        latitude: null,
        longitude: null,
        error: 'Geolocation is not supported by your browser',
      });
      return;
    }

    const success = (position) => {
      setLocation({
        latitude: position.coords.latitude,
        longitude: position.coords.longitude,
        error: null,
      });
    };

    const error = () => {
      setLocation({
        latitude: null,
        longitude: null,
        error: 'Unable to retrieve your location',
      });
    };

    navigator.geolocation.getCurrentPosition(success, error);
  }, []);

  return location;
}

export default useGeolocation;

使い方:

import useGeolocation from './useGeolocation';

function MyComponent() {
  const location = useGeolocation();

  return (
    <div>
      <p>Latitude: {location.latitude}</p>
      <p>Longitude: {location.longitude}</p>
      {location.error && <p>Error: {location.error}</p>}
    </div>
  );
}

useGeolocationを使うことで、地図アプリなど、位置情報を利用するアプリケーションを簡単に開発できます。

2.10 useMediaQuery: メディアクエリを監視するHook

useMediaQueryは、CSSのメディアクエリ(画面サイズやデバイスの種類に応じてスタイルを切り替える仕組み)を監視し、条件に合致するかどうかを判定するHookです。

// useMediaQuery.js
import { useState, useEffect } from 'react';

function useMediaQuery(query) {
  const [matches, setMatches] = useState(false);

  useEffect(() => {
    const mediaQuery = window.matchMedia(query);
    setMatches(mediaQuery.matches);

    const handleChange = () => {
      setMatches(mediaQuery.matches);
    };

    mediaQuery.addEventListener('change', handleChange);

    return () => {
      mediaQuery.removeEventListener('change', handleChange);
    };
  }, [query]);

  return matches;
}

export default useMediaQuery;

使い方:

import useMediaQuery from './useMediaQuery';

function MyComponent() {
  const isSmallScreen = useMediaQuery('(max-width: 768px)');

  return (
    <div>
      {isSmallScreen ? (
        <p>This is a small screen.</p>
      ) : (
        <p>This is a large screen.</p>
      )}
    </div>
  );
}

useMediaQueryを使うことで、画面サイズに応じてコンポーネントの表示を切り替えることができます。

3. まとめ:カスタムHooksでReact開発を加速させよう!

この記事では、React開発で役立つカスタムHooksを10個ご紹介しました。これらのHooksを使うことで、コードの再利用性を高め、コンポーネントをよりシンプルに保ち、開発速度を向上させることができます。

  • コードの再利用性: 同じロジックを複数のコンポーネントで共有できる。
  • コンポーネントの可読性: ロジックをHooksに分離することで、コンポーネントがシンプルになる。
  • 開発効率: 共通の処理を毎回書く必要がなくなるため、開発速度が向上する。

次のステップ:

  • この記事で紹介したカスタムHooksを実際に使ってみましょう。
  • 独自のカスタムHooksを作成してみましょう。
  • React Hooks APIについてさらに深く学びましょう。

カスタムHooksを使いこなして、React開発をさらに楽しんでください!