React Hooks 入門【代表的なフック解説】useState から useEffect まで初心者向けに解説
React Hooksとは何か、種類と使い方を初心者向けにやさしく解説。useState・useEffect・useContextなど代表的なフックを実例コード付きで紹介します。
「Reactを学び始めたら useState とか useEffect という不思議な関数が出てきて混乱した……」
そんな経験はありませんか?これらは React Hooks(フックス) と呼ばれる機能で、現代のReact開発では欠かせない存在です。
この記事では、Hooksの基本的な考え方から代表的なフックの役割まで、プログラミング初心者の方でもわかるように丁寧に解説します。
この記事でわかること:
- React Hooksとは何か・なぜ必要なのか
- 7つのカテゴリに分類された代表的なHooksの役割
- 最もよく使う
useStateuseEffectuseContextの使い方 - どのHooksから学べばいいかの学習ロードマップ
React Hooksって何?
React Hooksとは、関数コンポーネントの中でReactの機能を使えるようにする特別な関数です。
名前がすべて use で始まるのが特徴です(useState, useEffect など)。
Hooks登場前は何が大変だったの?
Hooks(React 16.8で登場)が登場する前は、状態管理や副作用処理をするためにはクラスコンポーネントという複雑な書き方が必要でした。
// Hooks登場前(クラスコンポーネント)- 複雑で読みにくい
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 }; // 状態はthis.stateに
}
render() {
return (
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
{this.state.count}回クリック
</button>
);
}
}// Hooks登場後(関数コンポーネント)- シンプルで読みやすい!
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
{count}回クリック
</button>
);
}Hooksのおかげで、コードがぐっとシンプルになりました。
Hooksを使うメリット・デメリット
◎ メリット
- ✓関数コンポーネントだけで完結するのでシンプルに書ける
- ✓ロジックをカスタムフックとして再利用できる
- ✓クラス構文(this)を覚えなくていい
- ✓テストが書きやすい
△ デメリット
- ✗ルール(条件分岐の中で使えない等)を守る必要がある
- ✗種類が多いので最初は覚えることが多い
- ✗useEffectの依存配列など、ハマりやすいポイントがある
Hooksの2つの大原則
Hooksを使うときに必ず守るルールが2つあります。
- コンポーネントのトップレベルでのみ呼び出す →
ifやforの中で使ってはいけない - React関数(コンポーネントまたはカスタムフック)の中でのみ呼び出す → 通常のJavaScript関数の中では使えない
// ❌ NG:条件分岐の中で使っている
function BadExample({ isLoggedIn }) {
if (isLoggedIn) {
const [name, setName] = useState(""); // エラーになる!
}
}
// ✅ OK:トップレベルで使う
function GoodExample({ isLoggedIn }) {
const [name, setName] = useState(""); // 常にトップレベルで
if (!isLoggedIn) return null;
return <p>{name}</p>;
}代表的なHooksを7カテゴリで解説
Hooksは公式ドキュメントで7つのカテゴリに分類されています。一つずつ見ていきましょう。
1. ステートフック(状態管理)
コンポーネントに「記憶」を持たせるためのHooksです。
useState ★最重要
最もよく使うHookです。シンプルな状態を管理します。
const [状態変数, 更新関数] = useState(初期値);function LikeButton() {
const [liked, setLiked] = useState(false); // 初期値はfalse
return (
<button onClick={() => setLiked(!liked)}>
{liked ? "❤️ いいね済み" : "🤍 いいね"}
</button>
);
}useReducer
状態の更新ロジックが複雑になってきたときに useState の代わりに使います。更新のルールを「reducer(リデューサー)」という関数にまとめられます。
// countのプラス・マイナスをreducerで管理する例
function reducer(state, action) {
switch (action.type) {
case "increment": return { count: state.count + 1 };
case "decrement": return { count: state.count - 1 };
default: return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<span>{state.count}</span>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
</>
);
}初心者へのアドバイス: まずは
useStateを使いこなしましょう。複雑になってきたらuseReducerに切り替えるイメージでOKです。
2. コンテキストフック(コンテキスト)
Props のバケツリレー問題を解決するためのHooksです。
useContext ★よく使う
コンポーネントツリーの深い部分にデータを渡したいとき、useContext を使うと中間のコンポーネントを経由せずに直接受け取れます。
// テーマ情報を親から子孫に渡す例
const ThemeContext = React.createContext("light");
// どこからでもテーマ情報を受け取れる!
function ChildComponent() {
const theme = useContext(ThemeContext);
return <div className={theme}>テーマ: {theme}</div>;
}
// 親コンポーネントでProviderでラップする
function App() {
return (
<ThemeContext.Provider value="dark">
<ChildComponent />
</ThemeContext.Provider>
);
}3. Refフック(参照)
レンダリングに影響しない値を保持したり、DOMの要素を直接操作するためのHooksです。
useRef
主に2つの用途で使います:
用途①:DOM要素を直接操作する(例:フォームにフォーカス当てる)
function SearchForm() {
const inputRef = useRef(null);
const handleClick = () => {
inputRef.current.focus(); // 検索ボックスにフォーカス!
};
return (
<>
<input ref={inputRef} type="text" placeholder="検索..." />
<button onClick={handleClick}>フォーカス</button>
</>
);
}用途②:再レンダリングを起こさずに値を保持する
function Timer() {
const timerIdRef = useRef(null); // タイマーIDを保存(変わっても再描画しない)
const start = () => {
timerIdRef.current = setInterval(() => console.log("tick"), 1000);
};
const stop = () => {
clearInterval(timerIdRef.current);
};
return (
<>
<button onClick={start}>開始</button>
<button onClick={stop}>停止</button>
</>
);
}useImperativeHandle
子コンポーネントのメソッドを親から呼び出せるようにするHookです。高度な使い方なので、最初は気にしなくてOKです。
4. エフェクトフック(副作用)
Reactの外の世界(API通信・タイマー・DOMの変更など)とやりとりするためのHooksです。
公式ドキュメントでは「エフェクトはReactパラダイムからの脱出ハッチ」と表現されています。必要なときだけ使いましょう。
useEffect ★重要
最もよく使うエフェクトです。コンポーネントが表示されたタイミングでデータを取得したり、外部システムと接続したりします。
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// コンポーネントが表示されたときにAPIを呼ぶ
fetch(`/api/users/${userId}`)
.then((res) => res.json())
.then((data) => setUser(data));
// クリーンアップ関数(コンポーネントが消えたときに実行)
return () => {
console.log("クリーンアップ");
};
}, [userId]); // [userId]:userIdが変わったときだけ再実行
return <div>{user?.name ?? "読み込み中..."}</div>;
}useEffect の第2引数(依存配列)がポイントです:
| 依存配列 | 実行タイミング |
|---|---|
| なし | 毎回のレンダリング後 |
[](空配列) | 初回表示時のみ |
[value] | value が変わったとき |
依存配列・クリーンアップ関数・よくあるトラブルなど、useEffect の詳しい解説は以下の記事をご覧ください。
→ useEffect 完全入門 - 副作用処理をやさしく解説
useLayoutEffect
useEffect とほぼ同じですが、ブラウザが画面を描画する前に実行されます。要素のサイズ計測など、描画タイミングが重要な処理に使います。通常は useEffect で十分です。
useInsertionEffect
CSS-in-JSライブラリ作者向けの特殊なHookです。一般的なアプリ開発では使いません。
5. パフォーマンスフック(最適化)
不要な再計算や再レンダリングを防ぐためのHooksです。パフォーマンスの問題が出てから使うのがベストプラクティスです。
useMemo
重い計算処理の結果をキャッシュします。同じ入力なら再計算せずにキャッシュを返します。
function DataTable({ items, filter }) {
// filterが変わったときだけ再計算する
const filteredItems = useMemo(
() => items.filter((item) => item.name.includes(filter)),
[items, filter]
);
return <ul>{filteredItems.map((item) => <li key={item.id}>{item.name}</li>)}</ul>;
}useCallback
関数の定義をキャッシュします。子コンポーネントに関数をpropsとして渡すときの不要な再レンダリングを防ぎます。
function Parent() {
const [count, setCount] = useState(0);
// countが変わっても handleClick の参照が変わらない
const handleClick = useCallback(() => {
console.log("クリック!");
}, []); // 依存なしなので一度だけ生成
return (
<>
<button onClick={() => setCount(count + 1)}>カウント: {count}</button>
<ChildButton onClick={handleClick} /> {/* 不要な再レンダリングを防ぐ */}
</>
);
}useTransition
状態の更新を優先度が低いものとしてマークします。入力フィールドなどの即時反応を維持しながら、重い更新処理を後回しにできます。
function SearchPage() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
setQuery(e.target.value); // 入力は即時反映
startTransition(() => {
setResults(heavySearch(e.target.value)); // 検索は後回しOK
});
};
return (
<>
<input value={query} onChange={handleChange} />
{isPending ? <p>検索中...</p> : <ResultList results={results} />}
</>
);
}useDeferredValue
値の更新を遅らせます。useTransition と似ていますが、こちらは値を遅らせる用途です。
6. その他のフック
useId
サーバーとクライアントで一致するユニークなIDを生成します。フォームのラベルとinputを紐付けるときなどに便利です。
function EmailField() {
const id = useId(); // 自動で一意のIDを生成
return (
<>
<label htmlFor={id}>メールアドレス</label>
<input id={id} type="email" />
</>
);
}useDebugValue
カスタムHookにDevTools用のラベルをつけられます。開発時のデバッグに使います。
useSyncExternalStore
ReactのstateではなくReduxなどの外部ストアを読み込むときに使います。ライブラリ作者向けです。
useActionState
フォームのアクション結果(成功/失敗状態)を管理するHookです。React 19から追加されました。
カスタムフック:自分でHooksを作れる
複数のHooksを組み合わせて、再利用可能なカスタムHookを作ることもできます。名前は必ず use から始めます。
// データ取得の処理をカスタムフックとして切り出す
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
})
.catch((err) => {
setError(err);
setLoading(false);
});
}, [url]);
return { data, loading, error };
}
// どのコンポーネントからも使いまわせる!
function UserList() {
const { data, loading } = useFetch("/api/users");
if (loading) return <p>読み込み中...</p>;
return <ul>{data.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}どのHooksから学べばいい?学習ロードマップ
まとめ
この記事で紹介した代表的なHooksを一覧でおさらいします。
| カテゴリ | Hook | 用途 | 優先度 |
|---|---|---|---|
| 状態 | useState | シンプルな状態管理 | ★★★ |
| 状態 | useReducer | 複雑な状態管理 | ★★ |
| コンテキスト | useContext | グローバルデータの受け取り | ★★★ |
| Ref | useRef | DOM操作・値の保持 | ★★ |
| エフェクト | useEffect | API通信・副作用処理 | ★★★ |
| パフォーマンス | useMemo | 計算結果のキャッシュ | ★ |
| パフォーマンス | useCallback | 関数のキャッシュ | ★ |
| その他 | useId | ユニークID生成 | ★ |
最初は useState → useEffect → useContext の3つをしっかり使いこなすことを目標にしてください。残りは必要になったタイミングで覚えれば大丈夫です!
PR
React公式ドキュメント(日本語)を読もう
ja.react.dev は日本語で読めるReact公式ドキュメント。各Hooksのインタラクティブなサンプルが充実していて、初心者にもおすすめです。
React公式ドキュメントを見る →