reactテンプレートファイルに貼り付けて動く
useState, useEffect, useRef, useMemo, useCallback, useReducer, useContext, createContext
の学習用
Code
import React, { useState, useEffect, useRef, useMemo, useCallback, useReducer, useContext, createContext } from 'react';
interface User{
name:string,
email:string
}
// 1. useContext用のコンテキスト
const ThemeContext = createContext<{ isDark: boolean; toggleTheme: () => void }>({
isDark: false,
toggleTheme: () => {},
});
// 2. useReducer用のreducer
const counterReducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
return state;
}
};
// useEffectの例 - データフェッチとクリーンアップ
const UseEffectExample = () => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// マウント時の処理
console.log('コンポーネントがマウントされました');
// 模擬的なAPIコール
const fetchUser = async () => {
setLoading(true);
// 実際のAPIの代わりにsetTimeoutでシミュレート
setTimeout(() => {
setUser({ name: '田中太郎', email: 'tanaka@example.com' });
setLoading(false);
}, 1000);
};
fetchUser();
// クリーンアップ関数
return () => {
console.log('useEffect クリーンアップ実行');
};
}, []); // 空の依存配列で初回のみ実行
if (loading) return <div className="p-4 text-blue-600">ユーザー情報を読み込み中...</div>;
return (
<div className="p-4 bg-blue-50 rounded-lg">
<h3 className="font-bold text-lg mb-2">useEffect例 - データフェッチ</h3>
<p><strong>名前:</strong> {user?.name}</p>
<p><strong>メール:</strong> {user?.email}</p>
</div>
);
};
// useRefの例 - DOM参照とフォーカス管理
const UseRefExample = () => {
const inputRef = useRef<HTMLInputElement | null>(null);
const [inputValue, setInputValue] = useState('');
const focusInput = () => {
inputRef.current?.focus();
};
const clearAndFocus = () => {
setInputValue('');
inputRef.current?.focus();
};
return (
<div className="p-4 bg-green-50 rounded-lg">
<h3 className="font-bold text-lg mb-2">useRef例 - DOM操作</h3>
<div className="space-y-2">
<input
ref={inputRef}
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="ここに入力してください"
className="w-full p-2 border rounded"
/>
<div className="space-x-2">
<button onClick={focusInput} className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600">
フォーカス
</button>
<button onClick={clearAndFocus} className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600">
クリア&フォーカス
</button>
</div>
<p className="text-sm text-gray-600">入力値: {inputValue}</p>
</div>
</div>
);
};
// useMemoの例 - 重い計算のメモ化
const UseMemoExample = () => {
const [numbers, setNumbers] = useState([1, 2, 3, 4, 5]);
const [multiplier, setMultiplier] = useState(1);
// 重い計算をシミュレート(実際はもっと複雑な処理)
const expensiveCalculation = useMemo(() => {
console.log('useMemo: 重い計算を実行中...');
return numbers.reduce((sum, num) => sum + (num * multiplier), 0);
}, [numbers, multiplier]); // numbersまたはmultiplierが変わった時のみ再計算
const addNumber = () => {
setNumbers(prev => [...prev, prev.length + 1]);
};
return (
<div className="p-4 bg-yellow-50 rounded-lg">
<h3 className="font-bold text-lg mb-2">useMemo例 - 重い計算のメモ化</h3>
<div className="space-y-2">
<p>数値: [{numbers.join(', ')}]</p>
<p>乗数: {multiplier}</p>
<p><strong>計算結果: {expensiveCalculation}</strong></p>
<div className="space-x-2">
<button onClick={addNumber} className="px-4 py-2 bg-yellow-500 text-white rounded hover:bg-yellow-600">
数値追加
</button>
<button onClick={() => setMultiplier(prev => prev + 1)} className="px-4 py-2 bg-orange-500 text-white rounded hover:bg-orange-600">
乗数+1
</button>
</div>
<p className="text-xs text-gray-500">※コンソールで計算実行のログを確認してください</p>
</div>
</div>
);
};
// useCallbackの例 - 関数のメモ化
const UseCallbackExample = () => {
const [count, setCount] = useState(0);
const [items, setItems] = useState(['アイテム1', 'アイテム2']);
// useCallbackで関数をメモ化
const addItem = useCallback(() => {
setItems(prev => [...prev, `アイテム${prev.length + 1}`]);
}, []); // 依存配列が空なので、この関数は初回作成時のみ
const resetItems = useCallback(() => {
setItems(['アイテム1', 'アイテム2']);
}, []);
return (
<div className="p-4 bg-purple-50 rounded-lg">
<h3 className="font-bold text-lg mb-2">useCallback例 - 関数のメモ化</h3>
<div className="space-y-2">
<p>カウント: {count}</p>
<p>アイテム数: {items.length}</p>
<ul className="list-disc list-inside">
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
<div className="space-x-2">
<button onClick={() => setCount(prev => prev + 1)} className="px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600">
カウント+1
</button>
<button onClick={addItem} className="px-4 py-2 bg-indigo-500 text-white rounded hover:bg-indigo-600">
アイテム追加
</button>
<button onClick={resetItems} className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600">
リセット
</button>
</div>
</div>
</div>
);
};
// useReducerの例 - 複雑な状態管理
const UseReducerExample = () => {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div className="p-4 bg-red-50 rounded-lg">
<h3 className="font-bold text-lg mb-2">useReducer例 - 複雑な状態管理</h3>
<div className="space-y-2">
<p className="text-2xl font-bold">カウント: {state.count}</p>
<div className="space-x-2">
<button
onClick={() => dispatch({ type: 'increment' })}
className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
>
+1
</button>
<button
onClick={() => dispatch({ type: 'decrement' })}
className="px-4 py-2 bg-pink-500 text-white rounded hover:bg-pink-600"
>
-1
</button>
<button
onClick={() => dispatch({ type: 'reset' })}
className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600"
>
リセット
</button>
</div>
</div>
</div>
);
};
// useContextの例 - テーマプロバイダー
const UseContextExample = () => {
const theme = useContext(ThemeContext);
return (
<div className={`p-4 rounded-lg ${theme?.isDark ? 'bg-gray-800 text-white' : 'bg-gray-100 text-black'}`}>
<h3 className="font-bold text-lg mb-2">useContext例 - テーマ管理</h3>
<p>現在のテーマ: {theme?.isDark ? 'ダーク' : 'ライト'}</p>
<button
onClick={theme?.toggleTheme}
className={`mt-2 px-4 py-2 rounded ${
theme?.isDark
? 'bg-white text-black hover:bg-gray-200'
: 'bg-black text-white hover:bg-gray-800'
}`}
>
テーマ切替
</button>
</div>
);
};
// メインコンポーネント
const HooksExamples = () => {
const [isDark, setIsDark] = useState(false);
const themeValue = {
isDark,
toggleTheme: () => setIsDark(prev => !prev)
};
return (
<ThemeContext.Provider value={themeValue}>
<div className="max-w-4xl mx-auto p-6 space-y-6">
<h1 className="text-3xl font-bold text-center mb-8">React Hooks 学習例集</h1>
<div className="grid gap-6">
<UseEffectExample />
<UseRefExample />
<UseMemoExample />
<UseCallbackExample />
<UseReducerExample />
<UseContextExample />
</div>
<div className="mt-8 p-4 bg-gray-50 rounded-lg">
<h3 className="font-bold text-lg mb-2">学習ポイント</h3>
<ul className="space-y-1 text-sm">
<li><strong>useState:</strong> 基本的な状態管理(この例では複数箇所で使用中)</li>
<li><strong>useEffect:</strong> 副作用の処理、データフェッチ、クリーンアップ</li>
<li><strong>useRef:</strong> DOM要素への参照、値の永続化</li>
<li><strong>useMemo:</strong> 重い計算結果のメモ化、パフォーマンス最適化</li>
<li><strong>useCallback:</strong> 関数のメモ化、子コンポーネントの不要な再レンダリング防止</li>
<li><strong>useReducer:</strong> 複雑な状態管理、Reduxライクなパターン</li>
<li><strong>useContext:</strong> コンポーネント間でのデータ共有</li>
</ul>
<h4 className="font-bold mt-4 mb-2">この例でuseStateが使われている箇所:</h4>
<ul className="space-y-1 text-xs text-gray-600">
<li>• UseEffectExample: user, loading状態の管理</li>
<li>• UseRefExample: inputValue状態の管理</li>
<li>• UseMemoExample: numbers, multiplier状態の管理</li>
<li>• UseCallbackExample: count, items状態の管理</li>
<li>• HooksExamples: isDarkテーマ状態の管理</li>
</ul>
</div>
</div>
</ThemeContext.Provider>
);
};
export default HooksExamples;