React Hooksのエラー原因と解決方法|初心者向け完全ガイド
React開発において、Hooksは状態管理や副作用の処理に欠かせない機能です。しかし、Hooksを使用する際にはルールがあり、ルールを守らないと様々なエラーが発生します。本記事では、React Hooksで発生しやすいエラーの原因と具体的な解決方法をコード例を交えて解説します。
React Hooksのエラーが発生する主な原因
React Hooksを使用する際に発生するエラーは、大きく分けて3つの原因があります。
1. 条件付きでHooksを呼び出している
React Hooksの最も重要なルールは「トップレベルのみで呼び出す」ことです。if文やループの中でHooksを呼び出すと、呼び出しの順序が変わり、Reactが状態を正しく管理できなくなります。
// ❌ エラーが発生するコード(条件付きでHooksを呼び出している)
function MyComponent() {
const [count, setCount] = useState(0);
if (count > 0) {
const [name, setName] = useState(''); // エラー!
}
return <div>{count}</div>;
}
2. ループやネストされた関数内でHooksを呼び出している
for文やwhile文、あるいは関数の内部でHooksを呼び出すと、エラーが発生します。これも呼び出し順序が変わるためです。
// ❌ エラーが発生するコード(ループ内でHooksを呼び出している)
function MyComponent() {
for (let i = 0; i < 3; i++) {
const [value, setValue] = useState(0); // エラー!
}
return <div>...</div>;
}
3. 関数コンポーネント以外でHooksを呼び出している
Hooksは関数コンポーネント内、またはカスタムHook内でのみ使用できます。クラスコンポーネントやJavaScriptの通常の関数内で使用するとエラーが発生します。
// ❌ エラーが発生するコード(クラスコンポーネント内)
class MyComponent extends React.Component {
componentDidMount() {
const [count, setCount] = useState(0); // エラー!
}
render() {
return <div>...</div>;
}
}
React Hooksのエラーを解決する手順
ステップ1: エラーメッセージを確認する
Reactはエラーが発生した際に、詳細なエラーメッセージをコンソールに出力します。まずは必ずブラウザの開発者ツール(DevTools)を開き、コンソールタブでエラーメッセージを確認しましょう。
よくあるエラーメッセージ:
- 「React Hook \”useState\” cannot be called inside a callback」
- 「React Hook \”useEffect\” is called conditionally」
- 「Hooks can only be called inside the body of a function component」
ステップ2: Hooksの呼び出し位置を確認する
エラーメッセージを確認した後は、コードのどこでHooksが呼び出されているのかを確認します。Hooksはコンポーネントの最上位のみで呼び出す必要があります。
ステップ3: コードを修正する
呼び出し位置が問題の場合は、Hooksをコンポーネントのトップレベルに移動させます。条件による分岐が必要な場合は、Hooksの内部で処理を分岐させます。
ステップ4: 修正後のテストを実施する
修正後は必ずブラウザをリロードし、エラーが解消されたことを確認しましょう。
実践的なコード例と解決方法
例1: 条件付きHooksのエラーを解決する
問題のあるコード:
function UserProfile({ userId }) {
// ❌ エラー:条件付きでHooksを呼び出している
if (userId) {
const [user, setUser] = useState(null);
}
return <div>...</div>;
}
修正されたコード:
function UserProfile({ userId }) {
// ✅ 修正:Hooksをトップレベルで呼び出す
const [user, setUser] = useState(null);
useEffect(() => {
if (userId) {
// ユーザー情報を取得する処理
fetchUser(userId).then(setUser);
}
}, [userId]);
return <div>{user ? user.name : 'ユーザーを選択してください'}</div>;
}
例2: ループ内でのHooksエラーを解決する
問題のあるコード:
function TodoList({ todos }) {
// ❌ エラー:ループ内でHooksを呼び出している
for (let todo of todos) {
const [isChecked, setIsChecked] = useState(false);
}
return <ul>...</ul>;
}
修正されたコード:
function TodoList({ todos }) {
// ✅ 修正:Hooksをコンポーネントレベルで管理
const [checkedStates, setCheckedStates] = useState(
todos.map(() => false)
);
const handleCheck = (index) => {
const newStates = [...checkedStates];
newStates[index] = !newStates[index];
setCheckedStates(newStates);
};
return (
<ul>
{todos.map((todo, index) => (
<li key={todo.id}>
<input
type=\"checkbox\"
checked={checkedStates[index]}
onChange={() => handleCheck(index)}
/>
{todo.title}
</li>
))}
</ul>
);
}
例3: useEffectでの一般的なエラーと解決法
問題のあるコード(無限ループ):
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
// ❌ エラー:依存配列がないため毎回実行される
fetch('/api/data')
.then(res => res.json())
.then(setData);
}); // 依存配列がない!
return <div>{data ? data.title : 'Loading...'}</div>;
}
修正されたコード:
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
// ✅ 修正:依存配列を指定して1度だけ実行
fetch('/api/data')
.then(res => res.json())
.then(setData);
}, []); // 空の依存配列を指定
return <div>{data ? data.title : 'Loading...'}</div>;
}
例4: カスタムHookでのエラー解決
カスタムHook内でのHooksの使用(正しい例):
// ✅ カスタムHook:useFetchは関数コンポーネント内でのみ使用可能
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 };
}
// useFetchの使用(正しい例)
function App() {
const { data, loading, error } = useFetch('/api/data');
if (loading) return <p>読み込み中...</p>;
if (error) return <p>エラーが発生しました</p>;
return <div>{data.title}</div>;
}
React Hooksで発生しやすい間違い
間違い1: useStateでの非同期処理
よくある間違い:
// ❌ 間違い:setState後すぐに値を使用しようとしている
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
console.log(count); // ここでは古い値が出力される
};
return <button onClick={increment}>{count}</button>;
}
正しい方法:
// ✅ 修正:useEffectで状態の変化を監視する
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('countが更新されました:', count);
}, [count]); // countが変更されたときのみ実行
const increment = () => {
setCount(count + 1);
};
return <button onClick={increment}>{count}</button>;
}
間違い2: useEffectの依存配列の誤り
よくある間違い:
// ❌ 間違い:依存配列にオブジェクトやアレイを直接入れている
function SearchUser({ filters }) {
useEffect(() => {
searchUsers(filters);
}, [filters]); // filtersが毎回新規作成される場合、無限ループになる
}
正しい方法:
// ✅ 修正:オブジェクトのプロパティを依存配列に指定
function SearchUser({ filters }) {
useEffect(() => {
searchUsers(filters);
}, [filters.keyword, filters.category]); // 個別のプロパティを指定
}
間違い3: Hooksの条件付き呼び出し
よくある間違い:
// ❌ 間違い:マウント後に条件分岐してHooksを呼び出している
function User({ id }) {
const [userLoaded, setUserLoaded] = useState(false);
if (!userLoaded) {
const [user, setUser] = useState(null); // エラー!
}
}
正しい方法:
// ✅ 修正:Hooksはトップレベルで宣言
function User({ id }) {
const [user, setUser] = useState(null);
useEffect(() => {
if (!user) {
fetchUser(id).then(setUser);
}
}, [id, user]);
return <div>{user ? user.name : 'Loading...'}</div>;
}
React Hooksのルールまとめ
エラーを防ぐために、必ず守るべきルールを再確認しましょう:
- ルール1:トップレベルのみで呼び出す – ループ、条件分岐、ネストされた関数内では呼び出さない
- ルール2:関数コンポーネント内でのみ使用 – クラスコンポーネント内では使用できない
- ルール3:Hooksの順序を保つ – 同じコンポーネント内で、毎回同じ順序でHooksが呼び出される必要がある
- ルール4:依存配列を正しく指定する – useEffect、useCallback、useMemoなどで依存配列を忘れずに指定
- ルール5:カスタムHookの命名規則 – カスタムHookは「use」で始まる名前にする
デバッグのコツ
ESLintプラグインの活用
React Hooksのルール違反を自動検出するために、ESLintプラグインの「eslint-plugin-react-hooks」を使用することをお勧めします。
npm install --save-dev eslint-plugin-react-hooks
// .eslintrc.json
{
\"plugins\": [\"react-hooks\"],
\"rules\": {
\"react-hooks/rules-of-hooks\": \"error\",
\"react-hooks/exhaustive-deps\": \"warn\"
}
}
React DevToolsの活用
ブラウザ拡張機能の「React DevTools」を使用すると、Hooksの状態変化をリアルタイムで監視できます。これはデバッグに非常に有効です。
まとめ
React Hooksのエラーは、ルールを理解して正しく使用すれば、ほとんどの場合で回避できます。本記事で紹介した重要なポイントをまとめます:
- Hooksはコンポーネントのトップレベルでのみ呼び出す
- 関数コンポーネント内またはカスタムHook内でのみ使用する
- useEffectの依存配列を正しく指定する
- setStateは非同期であることを理解する
- ESLintプラグインを使用して自動検出する
これらのルールを頭に入れておくことで、React Hooksの開発効率が大幅に向上します。エラーが発生した場合は、本記事で紹介した解決方法を参考にして、落ち着いて対処しましょう。React Hooksは強力な機能であり、正しく使用すれば、保守性の高い関数コンポーネントを作成できます。ぜひこの機会にマスターしてください。

