はじめに
「ウィンドウサイズを取得するフック、前のプロジェクトでも書いたな……」と思いながら、また同じようなuseWindowSizeを書いた経験はありませんか?
useLocalStorage、useDebounce、useToggle——Reactで開発していると、プロジェクトが変わるたびに似たようなカスタムフックを書き直しがちです。コピペで済ませても、微妙に挙動が違うバージョンがプロジェクトごとに散らばっていく。そんな「カスタムフックの車輪の再発明」を一気に解決してくれるのが、今回紹介するReact-Useです。
100個を超えるフックが1つのパッケージに詰まっており、GitHubスター数は44,000超。Reactフック集の定番として長く使われ続けているライブラリです。
とはいえ、読むより触った方が早いと思います。useCounterで数値を増減させるだけの小さなサンプルを用意したので、先に挙動を見たい方はこちらからどうぞ。
React-Useとは
React-Useは、React用のカスタムフックを網羅的に集めたユーティリティライブラリです。TypeScriptで書かれており、センサー系からアニメーション、状態管理まで、日常の開発で「あったらいいな」と思うフックがほぼ揃っています。
2026年6月リリースのv17.6.1まで257回のリリースを重ねており、現在も活発にメンテナンスされています。ライセンスはUnlicense(パブリックドメイン)で、商用利用も安心です。
主な特徴
- 圧倒的な網羅性 - 100個以上のフックが6つのカテゴリ(Sensors / UI / Animations / Side-effects / Lifecycles / State)に整理されています
- TypeScript製で型安全 - コードベースの99.5%がTypeScriptで書かれており、型定義が最初から付属します
- ツリーシェイキング対応 - 使ったフックだけがバンドルに含まれるため、100個入りでもバンドルサイズを心配する必要はありません
インストール
各パッケージマネージャーでインストールできます。
# npm
npm i react-use
# yarn
yarn add react-use
# pnpm
pnpm add react-use
追加の設定は一切不要です。インストールしたらすぐにインポートして使えます。
react-useのサンプルを動かす
まずは実際に動くサンプルで、react-useの雰囲気をつかんでみましょう。ここでは数値の増減・リセットを1行ずつのメソッドに切り出してくれるuseCounterフックを使います。下のプレイグラウンドはその場で編集・実行できるので、ボタンを押して結果がすぐ変わる様子を確かめてみてください。
useCounterは現在値と、inc(増加)・dec(減少)・reset(初期値に戻す)・set(任意の値に設定)といった操作関数をまとめて返します。要点だけを抜き出すと次のようになります。
import { useCounter } from 'react-use';
function Counter() {
const [count, { inc, dec, reset, set }] = useCounter(0, 10, 0);
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => inc()}>+1</button>
<button onClick={() => dec()}>-1</button>
<button onClick={() => set(5)}>5にセット</button>
<button onClick={reset}>リセット</button>
</div>
);
}
実際に動かせるものが下です。ボタンを押すと即座にカウントが変わり、上限(10)・下限(0)を超えるとそれ以上増減しなくなる挙動も確認できます。
上限に達すると+1や+3を押しても数値が止まる点に注目してください。useCounter(0, 10, 0)の第2引数(最大値)を20に変えれば上限が緩和されますし、第3引数(最小値)を変えればマイナス方向の挙動も試せます。数値操作をこの形でまとめておくと、自前でMath.min/Math.maxを書く手間がなくなります。
基本的な使い方
まずは最もシンプルなuseToggleから見てみましょう。モーダルの開閉やアコーディオンなど、ブール値の切り替えはUIの定番処理です。
import { useToggle } from 'react-use';
function Modal() {
const [isOpen, toggle] = useToggle(false);
return (
<div>
<button onClick={toggle}>モーダルを{isOpen ? '閉じる' : '開く'}</button>
{isOpen && <div className="modal">モーダルの中身です</div>}
</div>
);
}
useStateで書くとsetIsOpen(prev => !prev)のようなコードが必要ですが、useToggleならtoggleを呼ぶだけです。引数に値を渡せば明示的なセットもできます。
次に、レスポンシブ対応でよく使うuseWindowSizeです。
import { useWindowSize } from 'react-use';
function ResponsiveInfo() {
const { width, height } = useWindowSize();
return (
<p>
現在のウィンドウサイズ: {width} x {height}
</p>
);
}
リサイズイベントの登録・解除やクリーンアップ処理をすべてライブラリが引き受けてくれるので、コンポーネント側は「値を受け取るだけ」で済みます。
実践的なユースケース
ここからは、実際のプロジェクトで役立つ組み合わせを見ていきましょう。
1. useLocalStorageでテーマ設定を永続化する
ダークモードの設定など、リロードしても保持したい状態はuseLocalStorageが便利です。JSONのシリアライズ・デシリアライズも自動で行ってくれます。
import { useLocalStorage } from 'react-use';
function ThemeSwitcher() {
const [theme, setTheme] = useLocalStorage<'light' | 'dark'>('app-theme', 'light');
return (
<div data-theme={theme}>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
{theme === 'light' ? 'ダーク' : 'ライト'}モードに切り替え
</button>
</div>
);
}
2. useDebounceで検索入力を最適化する
入力のたびにAPIを叩くと無駄なリクエストが大量に発生します。useDebounceを使えば「入力が止まってから一定時間後に処理する」を数行で実現できます。
import { useState } from 'react';
import { useDebounce } from 'react-use';
function SearchBox() {
const [input, setInput] = useState('');
const [query, setQuery] = useState('');
useDebounce(
() => {
setQuery(input); // 入力が止まって500ms後に確定
},
500,
[input]
);
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="キーワードを入力"
/>
<p>検索クエリ: {query}</p>
</div>
);
}
3. useAsyncで非同期処理のローディング管理を任せる
データ取得時の「ローディング中・エラー・成功」の3状態管理は、自前で書くと意外と面倒です。useAsyncなら状態管理を丸ごと任せられます。
import { useAsync } from 'react-use';
function UserProfile({ userId }: { userId: string }) {
const state = useAsync(async () => {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('取得に失敗しました');
return response.json();
}, [userId]);
if (state.loading) return <p>読み込み中...</p>;
if (state.error) return <p>エラー: {state.error.message}</p>;
return <p>ユーザー名: {state.value.name}</p>;
}
4. useCopyToClipboardでコピー機能を実装する
コードスニペットや招待リンクの「コピーボタン」も1フックで完結します。
import { useCopyToClipboard } from 'react-use';
function CopyButton({ text }: { text: string }) {
const [state, copyToClipboard] = useCopyToClipboard();
return (
<button onClick={() => copyToClipboard(text)}>
{state.value === text ? 'コピーしました!' : 'コピー'}
</button>
);
}
5. useMediaでCSSメディアクエリをJSから使う
「モバイルではこのコンポーネント自体を描画しない」といった、CSSだけでは対応しづらい分岐もuseMediaならすっきり書けます。
import { useMedia } from 'react-use';
function Navigation() {
const isWide = useMedia('(min-width: 768px)');
return isWide ? <DesktopNav /> : <MobileNav />;
}
6. useHoverでホバー状態を検知する
「マウスが乗っている間だけスタイルを変える」というUIは、素のuseStateとonMouseEnter/onMouseLeaveでも書けますが、react-useのuseHoverを使うとレンダー関数を渡すだけで完結します。要点は次の通りです。
import { useHover } from 'react-use';
function HoverCard() {
const element = (hovered) => (
<div>{hovered ? 'ホバー中です' : 'ここにマウスを乗せてください'}</div>
);
const [hoverable, hovered] = useHover(element);
return (
<div>
{hoverable}
<p>状態: {hovered ? 'hovered' : 'unhovered'}</p>
</div>
);
}
実際に動かせるサンプルが下です。マウスを乗せたり離したりして、useHoverが返すhoveredの真偽値がリアルタイムに切り替わる様子を確認してみてください。
useHoverはマウスイベントの登録・解除を内部で完結させてくれるので、コンポーネント側はhoveredという1つの真偽値を受け取って表示を出し分けるだけです。カード内のスタイルを変えれば、ツールチップ表示やボタンのハイライトなど別のホバーUIにもそのまま応用できます。
7. useListで配列の状態操作をまとめる
配列をStateとして扱うとき、「1件追加」「1件削除」「全部クリア」のたびにsetStateでスプレッド構文を書くのは地味に手間です。useListを使うと、配列操作用の関数(push、removeAt、clear、resetなど)がまとめて手に入ります。
import { useList } from 'react-use';
function TodoList() {
const [list, { push, removeAt, clear, reset }] = useList<string>(['牛乳を買う']);
return (
<div>
<button onClick={() => push('新しいタスク')}>追加</button>
<button onClick={clear}>全削除</button>
<button onClick={reset}>リセット</button>
<ul>
{list.map((item, i) => (
<li key={i} onClick={() => removeAt(i)}>{item}</li>
))}
</ul>
</div>
);
}
実際に動かせるサンプルが下です。テキストを入力して「追加」を押すと配列にpushされ、リストの項目をクリックするとremoveAtでその項目だけ取り除けます。
push・removeAt・clear・resetはすべてuseListが生成した専用の更新関数なので、呼び出すたびに配列の不変更新(イミュータブルな更新)を自動でやってくれます。insertAtやupdateAtなど他の操作関数も用意されているため、Array.prototypeのメソッドをその都度書く必要がなくなります。
8. usePreviousで直前の値を保持する
「入力値が変わる前は何だったか」を知りたい場面は、フォームの差分表示やアニメーションのトリガー判定など意外とよくあります。usePreviousはレンダー間で1つ前の値を保持してくれるシンプルなフックです。
import { useState } from 'react';
import { usePrevious } from 'react-use';
function ValueTracker() {
const [value, setValue] = useState('react-use');
const previous = usePrevious(value);
return (
<div>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<p>現在の値: {value}</p>
<p>直前の値: {previous ?? '(まだありません)'}</p>
</div>
);
}
実際に動かせるサンプルが下です。入力欄に文字を打つたびに、「現在の値」と「直前の値」がそれぞれ更新されるタイミングのズレを確認できます。
1文字打つたびに「直前の値」が1つ前の状態のまま表示され、「現在の値」だけがリアルタイムに更新されるのが分かります。内部ではuseRefとuseEffectを組み合わせてレンダー後に値を更新しているだけですが、usePreviousとしてフック化しておくことで毎回同じロジックを書かずに済みます。
まとめ
React-Useは、100個超のフックを収録した「カスタムフックの百科事典」とも言えるライブラリです。
useToggleやuseWindowSizeなど、書きがちなフックが最初から揃っている- TypeScript製で型定義も完備、ツリーシェイキング対応でバンドルサイズも安心
- 44,000超のスターと257回のリリース実績があり、今も活発にメンテナンスされている
「このフック、自分で書くべきか?」と迷ったら、まずReact-Useのドキュメントを検索してみてください。たいていの場合、テスト済みの実装がすでに存在します。カスタムフックの車輪の再発明をやめて、その時間をプロダクト本来の機能開発に使いましょう。
