react-patterns
Vite + Cloudflare プロジェクト向けの React 19 パフォーマンスパターンおよびコンポーネント設計アーキテクチャを提供するスキルです。ウォーターフォールの排除・バンドル最適化・再レンダリング防止・boolean props に頼らない Composition パターン・サーバー/クライアント境界の設計・React 19 API の活用など、影響度順に並んだ 50 以上のルールを収録しています。React コンポーネントの実装・レビュー・リファクタリング時に活用でき、「react patterns」「react performance」「reduce re-renders」「fix waterfall」などをトリガーとして起動します。
description の原文を見る
React 19 performance patterns and composition architecture for Vite + Cloudflare projects. 50+ rules ranked by impact — eliminating waterfalls, bundle optimisation, re-render prevention, composition over boolean props, server/client boundaries, and React 19 APIs. Use when writing, reviewing, or refactoring React components. Triggers: 'react patterns', 'react review', 'react performance', 'optimise components', 'react best practices', 'composition patterns', 'why is it slow', 'reduce re-renders', 'fix waterfall'.
SKILL.md 本文
React Patterns
React 19 + Vite + Cloudflare Workers プロジェクト向けのパフォーマンスとコンポーネント構成パターン。新しいコンポーネント作成時のチェックリスト、既存コード監査時のレビューガイド、または何か遅かったり絡まっているように感じる場合のリファクタリングプレイブックとして使用します。
ルールは影響度でランク付けされています。MEDIUM なものに手を付ける前に CRITICAL な問題を修正してください。
適用のタイミング
- 新しい React コンポーネントやページを書くとき
- 既存コードのパフォーマンス問題をレビューするとき
- props が多すぎたり、再レンダーが多いコンポーネントをリファクタリングするとき
- 「なぜ遅いのか?」または「なぜこれが再レンダーされるのか?」をデバッグするとき
- 再利用可能なコンポーネント ライブラリを構築するとき
- マージ前のコードレビュー
1. ウォーターフォールの排除 (CRITICAL)
並列実行できるのに順序実行している非同期呼び出し。パフォーマンスの最大の敵です。
| パターン | 問題 | 修正方法 |
|---|---|---|
| 順序実行での await | const a = await getA(); const b = await getB(); | const [a, b] = await Promise.all([getA(), getB()]); |
| 子での fetch | 親が render → 子が fetch → 孫が fetch | fetch を最も近い共通祖先に上げ、データを下に渡す |
| Suspense の連鎖 | 複数の Suspense 境界が順序に resolve する | すべての非同期兄弟を囲む Suspense 境界を1つに |
| 分岐の前に await | const data = await fetch(); if (condition) { use(data); } | await を分岐内に移動 — 使わない可能性があるデータを fetch しない |
| import してから render | const Component = await import('./Heavy'); return <Component /> | React.lazy() + <Suspense> を使う — fallback を即座に render |
見つけ方: コンポーネント内で await を検索します。各 await がウォーターフォールの可能性です。2 つの await が独立していれば、並列実行すべきです。
2. バンドルサイズ (CRITICAL)
ユーザーがダウンロードする 1 KB は、待つ 1 KB です。
| パターン | 問題 | 修正方法 |
|---|---|---|
| Barrel import | import { Button } from '@/components' がバレルファイル全体を pull | import { Button } from '@/components/ui/button' — 直接 import |
| コード分割なし | 重いコンポーネントが全ページで読み込まれる | React.lazy(() => import('./HeavyComponent')) + <Suspense> |
| サードパーティが読み込み時 | Analytics/tracking がアプリ render 前に読み込まれる | hydration 後に読み込む: useEffect(() => { import('./analytics') }, []) |
| ライブラリ全体 import | import _ from 'lodash' (70KB) | import debounce from 'lodash/debounce' (1KB) |
| Lucide tree-shaking | import * as Icons from 'lucide-react' (全アイコン) | 明示的なマップ: import { Home, Settings } from 'lucide-react' |
| 重複した React | ライブラリが独自の React をバンドル → "Cannot read properties of null" | vite.config.ts で resolve.dedupe: ['react', 'react-dom'] |
見つけ方: npx vite-bundle-visualizer — バンドルに何が含まれているかを表示します。
3. コンポーネント構成アーキテクチャ (HIGH)
コンポーネントの構成方法は、最適化の方法よりも重要です。
| パターン | 問題 | 修正方法 |
|---|---|---|
| boolean prop の爆発 | <Card isCompact isClickable showBorder hasIcon isLoading> | 明示的なバリアント: <CompactCard>, <ClickableCard> |
| 複合コンポーネント | 15 個の prop を持つ複雑なコンポーネント | <Dialog>, <Dialog.Trigger>, <Dialog.Content> に分割 (共有 context 付き) |
| renderX props | <Layout renderSidebar={...} renderHeader={...} renderFooter={...}> | children + 名前付きスロットを使う: <Layout><Sidebar /><Header /></Layout> |
| 状態のリフト | 兄弟コンポーネント間で状態を共有できない | 状態を親または context provider に移動 |
| Provider 実装 | Consumer コードが状態管理の内部を知っている | Provider がインターフェース { state, actions, meta } を公開 — 実装は隠す |
| インラインコンポーネント | function Parent() { function Child() { ... } return <Child /> } | Child を Parent の外で定義 — インラインコンポーネントは render のたびに remount する |
テスト: コンポーネントが 5 個以上の boolean prop を持つなら、より多くの prop ではなく、コンポーネント構成が必要です。
4. 再レンダー防止 (MEDIUM)
すべての再レンダーが悪いわけではありません。目に見えるジャンクや無駄な計算を引き起こす再レンダーだけを修正します。
| パターン | 問題 | 修正方法 |
|---|---|---|
| デフォルト object/array props | function Foo({ items = [] }) → render のたびに新しい配列 ref | Hoist: const DEFAULT = []; function Foo({ items = DEFAULT }) |
| effect での派生状態 | useEffect(() => setFiltered(items.filter(...)), [items]) | render 中に派生: const filtered = useMemo(() => items.filter(...), [items]) |
| object 依存関係 | useEffect(() => {...}, [config]) → config が {} なら render のたびに発火 | プリミティブ deps を使う: useEffect(() => {...}, [config.id, config.type]) |
| 未使用の状態にサブスクライブ | { user, theme, settings } を読むが user だけ使う | context を分割またはセレクタを使う: useSyncExternalStore |
| 一時的な値を state に | const [mouseX, setMouseX] = useState(0) on mousemove | 頻繁に変わるが再レンダー不要な値は useRef を使う |
| インラインコールバック props | <Button onClick={() => doThing(id)} /> — render のたびに新しい関数 | useCallback または関数型 setState を使う: <Button onClick={handleClick} /> |
見つけ方: React DevTools Profiler → "Why did this render?" または <React.StrictMode> が開発時に double-render します。
5. React 19 固有事項 (MEDIUM)
React 19 で変わったまたは新しいパターン。
| パターン | 旧 (React 18) | 新 (React 19) |
|---|---|---|
| フォーム状態 | useFormState | useActionState — 名前変更 |
| Ref forwarding | forwardRef((props, ref) => ...) | function Component({ ref, ...props }) — ref は通常の prop |
| Context | useContext(MyContext) | use(MyContext) — 条件分岐とループ内で動作 |
| 保留中 UI | 手動ローディング状態 | useTransition + startTransition (非緊急更新用) |
| ルートレベル lazy | createBrowserRouter のみで動作 | <Route lazy={...}> は <BrowserRouter> では無視 |
| 楽観的更新 | 手動の状態管理 | useOptimistic hook |
| メタデータ | Helmet または手動 <head> 管理 | JSX で <title>, <meta>, <link> — 自動的に <head> に hoisted |
6. レンダリングパフォーマンス (MEDIUM)
| パターン | 問題 | 修正方法 |
|---|---|---|
| 読み込み時のレイアウトシフト | 非同期データ到着時にコンテンツが移動 | 最終的なレイアウト寸法と一致するスケルトン画面 |
| SVG を直接アニメーション | SVG アニメーションがぎこちない | <div> でラップし、div をアニメーション |
| 大規模リストレンダリング | テーブル/リストに 1000+ アイテム | @tanstack/react-virtual で仮想化レンダリング |
| content-visibility | 長いスクロール可能なコンテンツが最初にすべて render される | オフスクリーンセクションで content-visibility: auto |
| && での条件付き render | {count && <Items />} は count が 0 のときに 0 を render | ternary を使う: {count > 0 ? <Items /> : null} |
7. データ取得 (MEDIUM)
| パターン | 問題 | 修正方法 |
|---|---|---|
| 重複排除なし | 同じデータが 3 つのコンポーネントで fetch される | TanStack Query または SWR — 自動重複排除 + キャッシング |
| mount での fetch | useEffect(() => { fetch(...) }, []) — ウォーターフォール、キャッシュなし、重複排除なし | TanStack Query: useQuery({ queryKey: ['users'], queryFn: fetchUsers }) |
| 楽観的更新なし | ユーザーが save をクリック、2 秒待機、その後変更を確認 | useMutation と onMutate で即座に視覚的フィードバック |
| interval での古いクロージャ | setInterval が古い状態を capture | interval ID と現在の値に useRef を使う |
| cleanup なし polling | useEffect の setInterval に clearInterval がない | cleanup を返す: useEffect(() => { const id = setInterval(...); return () => clearInterval(id); }) |
8. Vite + Cloudflare 固有事項 (MEDIUM)
| パターン | 問題 | 修正方法 |
|---|---|---|
Node スクリプトでの import.meta.env | 未定義 — Vite 処理ファイルでのみ動作 | vite から loadEnv() を使う |
| React 重複インスタンス | ライブラリが独自の React をバンドル | vite.config.ts で resolve.dedupe + optimizeDeps.include |
| Radix Select 空文字列 | <SelectItem value=""> が throw | Sentinel を使う: <SelectItem value="__any__"> |
| React Hook Form null | {...field} が Input に null を渡す | 手動で spread: value={field.value ?? ''} |
| Edge でのenv vars | Workers に process.env が存在しない | c.env (Hono context) または import.meta.env (Vite build-time) を使う |
レビューチェックリストとしての使用
コードレビュー時、すべての PR でカテゴリ 1-3 (CRITICAL + HIGH) をチェックします。カテゴリ 4-8 はパフォーマンスが関心事の場合のみ。
/react-patterns [file or component path]
ファイルを読み、優先順でルールに照らし合わせ、結果を以下として報告します:
file:line — [rule] 問題の説明
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- jezweb
- リポジトリ
- jezweb/claude-skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/jezweb/claude-skills / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。