react について
react は、React アプリケーションを開発する人向けのリファレンススキルです。コンポーネントやフックの基本、パフォーマンス、アクセシビリティ、セキュリティ、テスト、トラブルシューティングを扱います。
主な機能
- 01関数コンポーネントと Props のパターン
- 02主要な React フック
- 03状態管理とコンポジションの指針
- 04パフォーマンス最適化
- 05アクセシビリティとセキュリティ対策
- 06テストとトラブルシューティング
活用シーン
- 01再利用可能な React コンポーネントの構成
- 02ローカル状態と共有状態の管理
- 03フックやレンダリング問題の診断
- 04アクセシビリティと本番運用準備の確認
MCP 対応
README では、technology `react` を指定して包括的な React ドキュメントを取得する mcp__documentation__fetch_docs を案内しています。
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill reactインストール後、Claude Code で/reactを入力して読み込まれたか確認します。
同梱ファイルquick-ref/component-patterns.mdquick-ref/hooks-cheatsheet.md
詳細
React コア知識
深い知識:フック、コンポーネント、React のパターンに関する包括的なドキュメントを参照するには、technology:
reactを指定してmcp__documentation__fetch_docsを使用してください。
コンポーネントのパターン
関数コンポーネント(推奨)
function UserCard({ name, email }: UserCardProps) {
return (
<div className="card">
<h2>{name}</h2>
<p>{email}</p>
</div>
);
}
Props の型指定
interface Props {
title: string;
count?: number;
children: React.ReactNode;
onClick: (id: string) => void;
}
基本的なフック
| フック | 目的 |
|---|---|
useState | ローカル状態 |
useEffect | 副作用、サブスクリプション |
useContext | コンテキスト値へのアクセス |
useRef | DOM の参照、可変値 |
useMemo | コストの高い計算をメモ化 |
useCallback | 関数をメモ化 |
useReducer | 複雑な状態ロジック |
主なパターン
- 継承よりコンポジション
- 共有状態には状態を上位に持ち上げる
- Props のバケツリレー → Context または状態管理ライブラリを使用
- 制御コンポーネントと非制御コンポーネントの入力
パフォーマンス
- コストの高い純粋コンポーネントには
React.memo()を使用します - 必要な場合にのみ
useMemo/useCallbackでメモ化します - リストでは
keyプロパティを正しく使用します React.lazy()とSuspenseで遅延読み込みします
本番環境への対応
セキュリティのベストプラクティス
// NEVER use dangerouslySetInnerHTML with user input
// BAD
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// GOOD - Sanitize with DOMPurify
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
// Avoid exposing sensitive data in client-side state
// BAD
const [apiKey, setApiKey] = useState(process.env.API_KEY);
// GOOD - API keys should stay server-side
// Use API routes or server actions instead
// Validate all external URLs
const isValidUrl = (url: string) => {
try {
const parsed = new URL(url);
return ['http:', 'https:'].includes(parsed.protocol);
} catch {
return false;
}
};
エラーバウンダリ
import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
// Usage
<ErrorBoundary
FallbackComponent={ErrorFallback}
onReset={() => window.location.reload()}
onError={(error, info) => {
// Log to error reporting service
logErrorToService(error, info);
}}
>
<App />
</ErrorBoundary>
パフォーマンスの最適化
// Code splitting with lazy loading
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<Skeleton />}>
<Dashboard />
</Suspense>
);
}
// Memoization - use sparingly, only for expensive components
const ExpensiveList = memo(function ExpensiveList({ items }: Props) {
return items.map(item => <ExpensiveItem key={item.id} {...item} />);
});
// Virtualization for large lists
import { FixedSizeList } from 'react-window';
function VirtualList({ items }: { items: Item[] }) {
return (
<FixedSizeList
height={400}
itemCount={items.length}
itemSize={50}
width="100%"
>
{({ index, style }) => (
<div style={style}>{items[index].name}</div>
)}
</FixedSizeList>
);
}
アクセシビリティ(a11y)
// Use semantic HTML
<button onClick={handleClick}>Submit</button> // NOT <div onClick>
// ARIA labels for icons/images
<button aria-label="Close dialog" onClick={onClose}>
<XIcon />
</button>
// Focus management
const dialogRef = useRef<HTMLDivElement>(null);
useEffect(() => {
dialogRef.current?.focus();
}, [isOpen]);
// Keyboard navigation
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
if (e.key === 'Tab') trapFocus(e);
};
テストのセットアップ
// Component testing with Testing Library
import { render, screen, userEvent } from '@testing-library/react';
test('submits form with user data', async () => {
const onSubmit = vi.fn();
render(<Form onSubmit={onSubmit} />);
await userEvent.type(screen.getByLabelText(/email/i), '[email protected]');
await userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(onSubmit).toHaveBeenCalledWith({ email: '[email protected]' });
});
モニタリング指標
| 指標 | アラートのしきい値 |
|---|---|
| 最大コンテンツ描画時間(LCP) | > 2.5s |
| 初回入力遅延(FID) | > 100ms |
| 累積レイアウトシフト(CLS) | > 0.1 |
| JavaScript バンドルサイズ | > 200KB(gzip 圧縮後) |
| コンポーネントのレンダリング時間 | > 16ms |
ビルドとバンドルの最適化
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown'],
},
},
},
sourcemap: true, // For error tracking in production
},
});
チェックリスト
- 重要なセクションをエラーバウンダリでラップする
- クライアントの状態に機密データを含めない
- HTML の描画には必ず DOMPurify を使用する
- ルート単位のコード分割に遅延読み込みを使用する
- 大規模なリスト(>100 項目)には仮想化を使用する
- セマンティック HTML と ARIA ラベルを使用する
- キーボードナビゲーションをサポートする
- Core Web Vitals をモニタリングする
- バンドルサイズを最適化する(<200KB gzip)
- 本番環境のデバッグ用にソースマップを用意する
- エラー報告サービスを統合する
このスキルを使用しない場合
- React 19 固有の機能(Actions、useActionState、use())— 代わりに
react-19スキルを使用してください - 高度なパフォーマンス最適化 — 代わりに
react-performanceスキルを使用してください - フォーム処理のパターン — 代わりに
react-formsまたはreact-hook-formスキルを使用してください - ルーティング — 代わりに
react-routerスキルを使用してください - テスト — 代わりに
react-testingスキルを使用してください - コンポーネント設計のパターン — 代わりに
react-patternsスキルを使用してください
アンチパターン
| アンチパターン | 問題 | 解決策 |
|---|---|---|
| 状態を直接変更する | 再レンダリングが発生しない | 新しいオブジェクト/配列を使って setState を呼び出す |
| 依存配列がない | 古いクロージャ、メモリリーク | すべての依存関係を含めるか、ESLint を使用する |
| 派生状態に useEffect を使う | 不要な再レンダリング | 描画中に計算するか、useMemo を使用する |
| Props を深くバケツリレーする | 保守が難しい | Context または状態管理ライブラリを使用する |
| JSX 内にオブジェクト/配列を直接記述する | メモ化が機能しなくなる | 定数に切り出すか、useMemo を使用する |
| レンダリング時に新しい関数を作成する | 子コンポーネントが不必要に再レンダリングされる | useCallback を使用する |
| useEffect のクリーンアップを忘れる | メモリリーク | クリーンアップ関数を返す |
| インデックスを key として使用する | 不適切な再レンダリング | 安定した一意の ID を使用する |
トラブルシューティング
| 問題 | 考えられる原因 | 対処法 |
|---|---|---|
| コンポーネントが再レンダリングされない | 状態の変更 | 新しい参照を使って setState を呼び出す |
| useEffect で無限ループが発生する | 依存関係がない、または誤っている | 依存関係を追加するか、関数型更新を使用する |
| 「Cannot read property of undefined」 | 非同期データが読み込まれていない | null チェックまたは読み込み状態を追加する |
| メモリリークの警告が出る | クリーンアップがない | useEffect からクリーンアップ関数を返す |
| 子要素が更新されない | インデックスを key として使用している | 安定した一意の ID を使用する |
| ハンドラーが実行されない | イベント伝播が停止されている | stopPropagation() の呼び出しを確認する |
| コールバック内の状態が古い | 古い状態を保持したクロージャ | 関数型 setState を使用する |
Reference Documentation
コメント
Markdown 対応