- はじめに
- そもそもReact Hooksってなに?
- useState - 状態を管理する
- useEffect - 副作用を実行する
- useContext - コンポーネント間でデータを共有する
- Hooks一覧まとめ
- まとめ
- 参考文献
はじめに
こんにちは!株式会社iimonエンジニアのクリスチャンです!
最近、暖かくなってきてアイスコーヒーが美味しい季節になってきましたね。
さて、今回は React Hooks について発表させていただきます!
Reactを書いていると必ず出てくるHooksですが、「なんとなく使っているけど、ちゃんと理解できているか不安……」という方も多いのではないでしょうか?(自分がそうです)
そこで今回は、よく使われるHooksを一つずつ整理してみました。
初心者目線でまとめているので、「Hooks何それ?」という方にも伝わるように頑張ります!
そもそもReact Hooksってなに?
React Hooksは、関数コンポーネントで状態管理や副作用などの機能を使えるようにする仕組みです。
React 16.8(2019年)で導入されました。
それ以前はクラスコンポーネントでしか状態管理ができなかったのですが、Hooksの登場により関数コンポーネントだけでほぼすべてのことができるようになりました。
Hooksには共通のルールがあります。
| ルール | 理由 |
|---|---|
| コンポーネントのトップレベルでのみ呼び出す | if文やfor文の中で呼ぶと、呼び出し順序が変わってしまいバグの原因になる |
| 関数コンポーネントまたはカスタムHook内でのみ呼び出す | 通常のJavaScript関数からは呼べない |
Hook名はuseで始める |
Reactが「これはHookだ」と認識するための命名規則 |
それでは、よく使われるHooksを見ていきましょう!
useState - 状態を管理する
概要
useStateは、コンポーネント内で状態(state)を持つための最も基本的なHookです。
ボタンを押したらカウントが増える、入力フォームの値を保持する、モーダルの開閉を管理する……こういった「変化する値」を扱うときに使います。
基本的な使い方
const [状態変数, 更新関数] = useState(初期値);
useStateは配列を返します。1つ目が現在の値、2つ目がその値を更新するための関数です。
コード例
import { useState } from "react"; const Counter = () => { const [count, setCount] = useState(0); return ( <div> <p>カウント: {count}</p> <button onClick={() => setCount(count + 1)}>+1</button> <button onClick={() => setCount(0)}>リセット</button> </div> ); };
よくある使い方いろいろ
// boolean(モーダルの開閉など) const [isOpen, setIsOpen] = useState(false); // 文字列(入力フォーム) const [name, setName] = useState(""); // オブジェクト(フォーム全体) const [form, setForm] = useState({ name: "", email: "" }); // オブジェクトの更新はスプレッド構文で! setForm({ ...form, name: "新しい名前" });
注意点
useStateの更新は非同期です。setCount(count + 1)を呼んでも、直後の行ではcountはまだ古い値のままです。
前の値を元に更新したい場合は、関数型アップデートを使いましょう。
// こうではなく setCount(count + 1); // こう書くと安全 setCount((prev) => prev + 1);
useEffect - 副作用を実行する
概要
useEffectは、レンダリングの後に副作用(side effect)を実行するためのHookです。
副作用とは、API通信、DOM操作、タイマー設定、イベントリスナーの登録など、「レンダリング以外の処理」のことです。
基本的な使い方
useEffect(() => { // 実行したい処理 return () => { // クリーンアップ処理(コンポーネントが消えるときに実行される) }; }, [依存配列]);
依存配列のパターン
| パターン | 実行タイミング |
|---|---|
useEffect(() => {}) |
毎回のレンダリング後 |
useEffect(() => {}, []) |
初回レンダリング後の1回だけ |
useEffect(() => {}, [a, b]) |
aまたはbが変化したとき |
コード例
APIからデータを取得する
import { useState, useEffect } from "react"; const UserList = () => { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { const fetchUsers = async () => { const response = await fetch("https://api.example.com/users"); const data = await response.json(); setUsers(data); setLoading(false); }; fetchUsers(); }, []); // 空配列 → 初回のみ実行 if (loading) return <p>読み込み中...</p>; return ( <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> ); };
クリーンアップが必要な例
useEffect(() => { const timer = setInterval(() => { console.log("1秒ごとに実行"); }, 1000); // コンポーネントがアンマウントされるときにタイマーを解除 return () => clearInterval(timer); }, []);
注意点
依存配列に入れ忘れた値があると、古い値を参照し続けるバグ(stale closure)が発生します。ESLintのreact-hooks/exhaustive-depsルールを有効にしておくと、入れ忘れを教えてくれます。
useContext - コンポーネント間でデータを共有する
概要
useContextは、propsを使わずにコンポーネントツリー全体でデータを共有するためのHookです。
「テーマ(ダークモード/ライトモード)」「ログインユーザー情報」「言語設定」など、多くのコンポーネントで必要なデータを渡すのに便利です。
propsのバケツリレー問題
通常、親から子にデータを渡すにはpropsを使います。でも、深くネストしたコンポーネントに渡したい場合、途中のコンポーネントが使いもしないpropsを受け取って次に渡す……ということが起きます。これをProps Drilling(バケツリレー)と呼びます。
App → Header → Navigation → UserMenu → UserName
↑ userをここまでpropsで渡す必要がある……つらい
useContextを使えば、途中のコンポーネントを飛ばしてデータにアクセスできます。
コード例
import { createContext, useContext, useState } from "react"; // 1. Contextを作成 const ThemeContext = createContext(undefined); // 2. Providerコンポーネントを作成 const ThemeProvider = ({ children }) => { const [theme, setTheme] = useState("light"); const toggleTheme = () => { setTheme((prev) => (prev === "light" ? "dark" : "light")); }; return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); }; // 3. カスタムHookで使いやすくする const useTheme = () => { const context = useContext(ThemeContext); if (!context) { throw new Error("useThemeはThemeProvider内で使ってください"); } return context; }; // 4. 子コンポーネントで使う(何階層深くてもOK) const ThemedButton = () => { const { theme, toggleTheme } = useTheme(); return ( <button onClick={toggleTheme} style={{ background: theme === "dark" ? "#333" : "#fff", color: theme === "dark" ? "#fff" : "#333", }} > 現在のテーマ: {theme}(クリックで切り替え) </button> ); }; // 5. Providerで囲んで使う const App = () => { return ( <ThemeProvider> <ThemedButton /> </ThemeProvider> ); };
注意点
Contextの値が変わると、そのContextを使っているすべてのコンポーネントが再レンダリングされます。頻繁に更新される値(マウス位置など)をContextに入れるとパフォーマンスに影響が出ることがあるので注意しましょう。
Hooks一覧まとめ
| Hook | 一言で説明 | よくある使い所 |
|---|---|---|
useState |
状態を持つ | カウンター、フォーム入力、モーダル開閉 |
useEffect |
副作用を実行する | API通信、タイマー、イベントリスナー |
useContext |
データをグローバルに共有 | テーマ、認証情報、言語設定 |
まとめ
今回は、よく使われるReact Hooksを3つ紹介しました。
正直、自分もまだ完璧に理解できているわけではなく、「この場面ではどれを使うべき?」と迷うことも多いです。
ただ、こうやって整理してみると、それぞれの役割がはっきり見えてきた気がします。
個人的に大事だなと思ったポイントをまとめると:
useState→ 画面に反映したい「変化する値」を管理する基本中の基本useEffectの依存配列 → 入れ忘れに注意。ESLintに頼ろうuseContext→ バケツリレーが辛くなったら導入を検討
まずはuseStateとuseEffectをしっかり使いこなすところから始めて、必要に応じてuseContextを取り入れていくのが良さそうですね。
ここまで読んでいただきありがとうございます!
下記リンクよりご応募お待ちしております!
iimon採用サイト / Wantedly / Green