React Hooksを使いこなす - 実践的なパターン集
React Hooksとは
React Hooksは、関数コンポーネントでstateやライフサイクルなどのReactの機能を使えるようにする仕組みです。クラスコンポーネントを使わずに、よりシンプルなコードが書けるようになりました。
基本のHooks
useState - 状態管理
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount(count + 1)}>
増やす
</button>
</div>
);
}
複数の状態を管理する場合:
function UserForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
// またはオブジェクトで管理
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
const handleChange = (field, value) => {
setUser(prev => ({
...prev,
[field]: value
}));
};
}
useEffect - 副作用の処理
import { useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// データの取得
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
// クリーンアップ関数(オプショナル)
return () => {
// コンポーネントのアンマウント時に実行
console.log('Cleanup');
};
}, [userId]); // userIdが変わった時のみ実行
if (!user) return <div>読み込み中...</div>;
return <div>{user.name}</div>;
}
useCallback - 関数のメモ化
import { useCallback } from 'react';
function TodoList() {
const [todos, setTodos] = useState([]);
// 関数をメモ化して不要な再生成を防ぐ
const addTodo = useCallback((text) => {
setTodos(prev => [...prev, { id: Date.now(), text }]);
}, []); // 依存配列が空なので、関数は一度だけ生成される
const removeTodo = useCallback((id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
}, []);
return (
<div>
<TodoInput onAdd={addTodo} />
<TodoItems items={todos} onRemove={removeTodo} />
</div>
);
}
useMemo - 計算結果のメモ化
import { useMemo } from 'react';
function ExpensiveComponent({ items }) {
// 重い計算をメモ化
const sortedItems = useMemo(() => {
console.log('ソート処理実行');
return items.sort((a, b) => a.value - b.value);
}, [items]); // itemsが変わった時のみ再計算
return (
<ul>
{sortedItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
カスタムHooks
独自のHooksを作成することで、ロジックを再利用できます:
useLocalStorage - ローカルストレージ管理
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;
}
});
const setValue = (value) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
// 使用例
function App() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
テーマ切り替え: {theme}
</button>
);
}
useFetch - データ取得
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setError(null);
})
.catch(err => {
setError(err);
setData(null);
})
.finally(() => {
setLoading(false);
});
}, [url]);
return { data, loading, error };
}
// 使用例
function UserList() {
const { data, loading, error } = useFetch('/api/users');
if (loading) return <div>読み込み中...</div>;
if (error) return <div>エラー: {error.message}</div>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
useDebounce - デバウンス処理
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// 使用例:検索ボックス
function SearchBox() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearchTerm) {
// API呼び出しは0.5秒後に実行される
fetch(`/api/search?q=${debouncedSearchTerm}`)
.then(res => res.json())
.then(data => console.log(data));
}
}, [debouncedSearchTerm]);
return (
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="検索..."
/>
);
}
Hooksのルール
- トップレベルでのみ呼び出す:ループ、条件分岐、ネストした関数の中ではHooksを呼び出さない
- Reactの関数内でのみ呼び出す:通常のJavaScript関数からはHooksを呼び出さない
まとめ
React Hooksを使うことで、コンポーネントのロジックをシンプルに保ちながら、再利用可能な形で実装できます。カスタムHooksを活用することで、よりクリーンで保守性の高いコードが書けるようになります。
ぜひ自分のプロジェクトでHooksを活用してみてください!