# 📝 基本文法ガイド GitHub Power Scouterで使用されているReact + TypeScriptの基本文法を、初心者にも分かりやすく実際のコードサンプルと共に解説します。 ## 🎯 React基本概念 ### 🧩 コンポーネントとは ```typescript // 関数コンポーネント(推奨) const UserInput: React.FC = () => { return (
); }; // TypeScript型定義付きコンポーネント interface UserInputProps { onScan: (username: string) => void; isScanning: boolean; } const UserInput: React.FC = ({ onScan, isScanning }) => { return (
); }; ``` ### 🎛️ JSX基本記法 ```typescript // JSX基本構文 const ScouterDisplay = () => { const powerLevel = 9001; const isScanning = true; return (
{/* コメント記法 */}

Power Level: {powerLevel}

{/* 条件分岐 */} {isScanning ? (

Scanning in progress...

) : (

Scan complete!

)} {/* 配列のレンダリング */} {['JavaScript', 'TypeScript', 'React'].map(skill => ( {skill} ))} {/* イベントハンドラー */}
); }; ``` ## 🪝 React Hooks ### 📊 useState - 状態管理 ```typescript import { useState } from 'react'; const UserInput = () => { // 基本的な状態 const [username, setUsername] = useState(''); const [isScanning, setIsScanning] = useState(false); // 複合的な状態 const [user, setUser] = useState<{ name: string; followers: number; } | null>(null); // 配列の状態 const [languages, setLanguages] = useState([]); const handleScan = () => { setIsScanning(true); // 非同期処理の例 setTimeout(() => { setUser({ name: 'Test User', followers: 1000, }); setLanguages(['JavaScript', 'TypeScript']); setIsScanning(false); }, 2000); }; return (
setUsername(e.target.value)} placeholder="Enter username" /> {user && (

{user.name}

Followers: {user.followers}

    {languages.map(lang => (
  • {lang}
  • ))}
)}
); }; ``` ### 🎭 useEffect - 副作用処理 ```typescript import { useState, useEffect } from 'react'; const ScouterDisplay = ({ username }: { username: string }) => { const [userData, setUserData] = useState(null); const [loading, setLoading] = useState(false); // コンポーネントマウント時の処理 useEffect(() => { console.log('Component mounted'); // クリーンアップ関数 return () => { console.log('Component unmounted'); }; }, []); // 空の依存配列 = マウント時のみ実行 // 特定の値が変更された時の処理 useEffect(() => { if (!username) return; setLoading(true); const fetchUser = async () => { try { const response = await fetch(`/api/users/${username}`); const data = await response.json(); setUserData(data); } catch (error) { console.error('Error fetching user:', error); } finally { setLoading(false); } }; fetchUser(); }, [username]); // username が変更される度に実行 // タイマーの例 useEffect(() => { const timer = setInterval(() => { console.log('Timer tick'); }, 1000); return () => clearInterval(timer); // クリーンアップ }, []); return (
{loading ? (

Loading...

) : (
{userData &&

User: {userData.name}

}
)}
); }; ``` ### 🧠 useCallback & useMemo - パフォーマンス最適化 ```typescript import { useState, useCallback, useMemo } from 'react'; const PowerLevelCalculator = ({ userData, repoData }) => { const [multiplier, setMultiplier] = useState(1); // 重い計算をメモ化 const powerLevel = useMemo(() => { console.log('Calculating power level...'); const basePower = ( userData.public_repos * 100 + userData.followers * 50 + userData.following * 10 ); return basePower * multiplier; }, [userData, multiplier]); // 依存配列が変更時のみ再計算 // 関数をメモ化 const handleScan = useCallback(() => { console.log('Scanning user...'); // スキャン処理 }, [userData]); // userData が変更時のみ新しい関数を生成 // 配列の処理をメモ化 const sortedLanguages = useMemo(() => { return repoData .flatMap(repo => repo.languages) .sort((a, b) => b.bytes - a.bytes); }, [repoData]); return (

Power Level: {powerLevel.toLocaleString()}

    {sortedLanguages.map(lang => (
  • {lang.name}
  • ))}
); }; ``` ## 🔧 TypeScript基本文法 ### 🏷️ 型定義 ```typescript // 基本的な型 let username: string = 'torvalds'; let powerLevel: number = 9001; let isScanning: boolean = true; // 配列の型 let languages: string[] = ['JavaScript', 'TypeScript']; let numbers: Array = [1, 2, 3]; // オブジェクトの型 interface GitHubUser { login: string; id: number; name: string | null; // null許容型 followers: number; following: number; public_repos: number; created_at: string; bio?: string; // オプショナル(undefined許容) } // 型エイリアス type PowerLevel = number; type UserData = GitHubUser; // Union型 type Theme = 'light' | 'dark' | 'auto'; type Status = 'loading' | 'success' | 'error'; // 関数の型 type ScanFunction = (username: string) => Promise; type EventHandler = (event: React.MouseEvent) => void; ``` ### 🎨 インターフェース vs 型エイリアス ```typescript // インターフェース(推奨:オブジェクト定義) interface GitHubRepo { id: number; name: string; full_name: string; stargazers_count: number; language: string | null; } // インターフェースの拡張 interface DetailedRepo extends GitHubRepo { description: string; topics: string[]; contributors: GitHubUser[]; } // 型エイリアス(推奨:Union型、計算型) type ApiResponse = { data: T; status: 'success' | 'error'; message?: string; }; type UserApiResponse = ApiResponse; type RepoApiResponse = ApiResponse; // 条件付き型 type NonNullable = T extends null | undefined ? never : T; type UserName = NonNullable; ``` ### 🛡️ 型ガード ```typescript // ユーザー定義型ガード function isGitHubUser(data: any): data is GitHubUser { return ( typeof data === 'object' && data !== null && typeof data.login === 'string' && typeof data.id === 'number' ); } // 使用例 const fetchUser = async (username: string): Promise => { const response = await fetch(`/api/users/${username}`); const data = await response.json(); if (isGitHubUser(data)) { return data; // 型が確定 } throw new Error('Invalid user data'); }; // in演算子を使った型ガード type LoadingState = { status: 'loading' }; type SuccessState = { status: 'success'; data: GitHubUser }; type ErrorState = { status: 'error'; message: string }; type AppState = LoadingState | SuccessState | ErrorState; const handleState = (state: AppState) => { if ('data' in state) { // SuccessState として扱われる console.log(state.data.login); } else if ('message' in state) { // ErrorState として扱われる console.error(state.message); } else { // LoadingState として扱われる console.log('Loading...'); } }; ``` ## 🎯 実践的なパターン ### 🔄 カスタムフック ```typescript // GitHub API用カスタムフック const useGitHubApi = () => { const [token, setToken] = useState( localStorage.getItem('github_token') ); const getHeaders = useCallback((): HeadersInit => { return token ? { Authorization: `token ${token}` } : {}; }, [token]); const fetchUser = useCallback(async (username: string): Promise => { const response = await fetch(`https://api.github.com/users/${username}`, { headers: getHeaders(), }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.json(); }, [getHeaders]); const fetchRepos = useCallback(async (username: string): Promise => { const response = await fetch(`https://api.github.com/users/${username}/repos`, { headers: getHeaders(), }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.json(); }, [getHeaders]); return { token, setToken, fetchUser, fetchRepos, }; }; // 使用例 const UserProfile = ({ username }: { username: string }) => { const { fetchUser } = useGitHubApi(); const [user, setUser] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; const loadUser = async () => { setLoading(true); setError(null); try { const userData = await fetchUser(username); if (!cancelled) { setUser(userData); } } catch (err) { if (!cancelled) { setError(err instanceof Error ? err.message : 'Unknown error'); } } finally { if (!cancelled) { setLoading(false); } } }; loadUser(); return () => { cancelled = true; }; }, [username, fetchUser]); if (loading) return
Loading...
; if (error) return
Error: {error}
; if (!user) return
No user data
; return (

{user.name || user.login}

Followers: {user.followers}

Following: {user.following}

); }; ``` ### 🎨 コンポーネント合成 ```typescript // 基本コンポーネント const Button: React.FC<{ variant?: 'primary' | 'secondary'; size?: 'small' | 'medium' | 'large'; disabled?: boolean; onClick?: () => void; children: React.ReactNode; }> = ({ variant = 'primary', size = 'medium', disabled = false, onClick, children }) => { const className = `button button--${variant} button--${size}`; return ( ); }; // 合成コンポーネント const ScanButton: React.FC<{ isScanning: boolean; onScan: () => void; }> = ({ isScanning, onScan }) => { return ( ); }; // 高階コンポーネント(HOC) const withLoading =

( Component: React.ComponentType

) => { return (props: P & { isLoading: boolean }) => { const { isLoading, ...rest } = props; if (isLoading) { return

Loading...
; } return ; }; }; // 使用例 const UserProfileWithLoading = withLoading(UserProfile); ``` ### 🔄 状態管理パターン ```typescript // Reducer パターン type State = { user: GitHubUser | null; repos: GitHubRepo[]; loading: boolean; error: string | null; }; type Action = | { type: 'FETCH_START' } | { type: 'FETCH_USER_SUCCESS'; payload: GitHubUser } | { type: 'FETCH_REPOS_SUCCESS'; payload: GitHubRepo[] } | { type: 'FETCH_ERROR'; payload: string } | { type: 'RESET' }; const initialState: State = { user: null, repos: [], loading: false, error: null, }; const userReducer = (state: State, action: Action): State => { switch (action.type) { case 'FETCH_START': return { ...state, loading: true, error: null }; case 'FETCH_USER_SUCCESS': return { ...state, user: action.payload, loading: false }; case 'FETCH_REPOS_SUCCESS': return { ...state, repos: action.payload, loading: false }; case 'FETCH_ERROR': return { ...state, error: action.payload, loading: false }; case 'RESET': return initialState; default: return state; } }; // 使用例 const UserDashboard = () => { const [state, dispatch] = useReducer(userReducer, initialState); const { fetchUser, fetchRepos } = useGitHubApi(); const handleFetchUser = useCallback(async (username: string) => { dispatch({ type: 'FETCH_START' }); try { const user = await fetchUser(username); dispatch({ type: 'FETCH_USER_SUCCESS', payload: user }); const repos = await fetchRepos(username); dispatch({ type: 'FETCH_REPOS_SUCCESS', payload: repos }); } catch (error) { dispatch({ type: 'FETCH_ERROR', payload: error instanceof Error ? error.message : 'Unknown error' }); } }, [fetchUser, fetchRepos]); return (
{state.loading &&
Loading...
} {state.error &&
Error: {state.error}
} {state.user && (

{state.user.name}

Repos: {state.repos.length}

)}
); }; ``` ## 🎯 実践課題 ### 🏆 初級課題 1. **基本コンポーネント作成** - ユーザー情報を表示するコンポーネント - Props でデータを受け取る - 条件分岐でローディング状態を表示 2. **状態管理** - useState でフォーム入力を管理 - useEffect で API 呼び出し - エラーハンドリング ### 🚀 中級課題 1. **カスタムフック作成** - GitHub API 呼び出し用フック - ローディング、エラー状態の管理 - TypeScript 型定義 2. **パフォーマンス最適化** - useMemo で重い計算をメモ化 - useCallback で関数をメモ化 - React.memo でコンポーネントをメモ化 ### 💪 上級課題 1. **高階コンポーネント** - エラーバウンダリー実装 - ローディング状態のラッパー - 認証チェック HOC 2. **複雑な状態管理** - useReducer で複雑な状態管理 - Context API でグローバル状態 - 型安全なアクション定義 --- **次のステップ**: [Tips & Best Practices](Tips-and-Best-Practices.md)で実践的な開発テクニックを学びましょう。