mcpField
I
ideal-react-component

ideal-react-component

github.com/antjanus開発281 回インストール2 週間前に更新
リポジトリ

ideal-react-component について

React 開発者向けに、関数コンポーネントを7つのセクションに整理する方法と、よくある Hooks のアンチパターンを解説するスキルです。状態ロジックをカスタムフックへ切り出す目安も示します。

主な機能

  1. 01依存順に沿った7セクション構成
  2. 02よくある Hooks のアンチパターン
  3. 03コンポーネントのリファクタリングとフック抽出
  4. 04早期 return とフック呼び出し順の説明
  5. 05必要に応じて参照できる詳細資料

活用シーン

  1. 01React 関数コンポーネントの構成統一
  2. 02再レンダーや古い状態の問題調査
  3. 03複雑なロジックのカスタムフックへの抽出

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/antjanus/skillbox --skill ideal-react-component

インストール後、Claude Code で/ideal-react-componentを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

reference/
COMPLETE-EXAMPLES.md
EVAL.md
HOOKS-ANTIPATTERNS.md
REFACTORING.md
SECTIONS.md

詳細

理想的な React コンポーネント構造

概要

関数コンポーネントのファイルを、インポート → スタイル → 型 → コンポーネント → ロジック → 条件付きレンダリング → デフォルトレンダリングの、予測しやすい7つのセクションに並べます。これにより、ファイルを開いた人がどこを見ればよいかすぐにわかります。また、無限ループや古い状態の主な原因となる、フックのアンチパターンも取り上げます。

基本原則: ファイルは依存関係の順に上から下へ読み進められ、ハッピーパスは最も目につく最下部に配置します。これはパターンであり、絶対的な規則ではありません。小さなコンポーネントや React Server Components では、該当するセクションを省略しても問題ありません。

7つのセクション構造

// 1. IMPORTS (grouped: React → third-party → internal @/ → local, blank line between)
import React, { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import styled from 'styled-components';
import { api } from '@/services/api';
import { Button } from './Button';

// 2. STYLED COMPONENTS (prefix "Styled" so they're instantly recognizable)
const StyledContainer = styled.div`padding: 1rem; background: white;`;

// 3. TYPE DEFINITIONS (ComponentNameProps, declared right above the component)
type UserProfileProps = {
  userId: string;
  onUpdate?: (user: User) => void;
};

// 4. COMPONENT FUNCTION (named export, const arrow function)
export const UserProfile = ({ userId, onUpdate }: UserProfileProps): React.JSX.Element => {
  // 5. LOGIC
  const [isEditing, setIsEditing] = useState(false);                        // 5.1 local state
  const { data, isLoading, error } = useQuery({ queryKey: ['user', userId], queryFn: () => api.getUser(userId) }); // 5.2 custom/data hooks
  useEffect(() => { /* focus the editor */ }, [isEditing]);                 // 5.3 effects
  const displayName = data ? `${data.first} ${data.last}` : '';            // 5.4 post-processing
  const handleEdit = () => setIsEditing(true);                             // 5.5 handlers

  // 6. CONDITIONAL RENDERING (exit early for each edge case, after every hook call)
  if (isLoading) return <Loading />;
  if (error) return <Error message={error.message} />;
  if (!data) return <Empty />;

  // 7. DEFAULT RENDER (success/happy path stays at the bottom — most visible)
  return <StyledContainer>{/* Main JSX */}</StyledContainer>;
};

状態を最初に、依存するフックの後にエフェクトを、最後にハンドラーを配置します。これにより、各行はそれより上で宣言されたものだけを参照します。

JavaScript: 型注釈なしで同じパターンを使います。セクション3を省略するか、JSDoc を使ってください。

セクション内容理由
1. インポートReact、ライブラリ、内部の @/、ローカルのもの依存関係を見つけやすくします
2. スタイルStyled コンポーネント(接頭辞は Styled)、Tailwind、CSS Modulesロジックと視覚的に分離できます
3. 型コンポーネントの上に *Props、*ReturnAPIをひと目で確認できます
4. コンポーネントexport const Component = (...) =>名前付きエクスポートはリファクタリングしやすくなります
5. ロジック状態 → フック → エフェクト → 後処理 → ハンドラーフックのルールに従い、依存元を依存先より先に配置できます
6. 条件付きレンダリング読み込み中/エラー/空の状態に対する早期リターンネストを減らし、ガードの後で型を絞り込めます
7. デフォルトレンダリング成功状態の JSXハッピーパスが最も目につくコードになります

よくあるフックのアンチパターン

無限ループ、古いデータ、予期しない再レンダリングの最も一般的な原因を3つ紹介します。まず望ましい形式を示します。

1. レンダリング中に値を導出し、エフェクトで同期しない。 他の状態から状態を計算するだけのエフェクトは、余分なレンダーパスを発生させます。また、親がその値を再び渡すと、無限ループが発生します。

const fullName = `${first} ${last}`;                                   // ✅ derive during render
useEffect(() => { setFullName(`${first} ${last}`); }, [first, last]); // ❌ extra render, loop risk

2. key でプロパティ由来の状態をリセットし、useState がプロパティを追跡することを期待しない。 初期化関数は1回しか実行されないため、プロパティが変わっても状態は古いままです。

<Component key={itemId} initialValue={data.value} />      // ✅ remount resets state
const [value, setValue] = useState(props.initialValue);   // ❌ stale after prop changes

3. 依存関係をすべて列挙する。 依存関係を省略すると、古いクロージャーが取り込まれ、エフェクトは最初のレンダリング時の値を読み続けます。

useEffect(() => { setTotal(count * price); }, [count, price]);   // ✅ all deps
useEffect(() => { setTotal(count * price); }, [price]);          // ❌ missing count

リファクタリング

コンポーネントのロジックが約50行、または全体が約200行を超えたら、状態を持つロジックを use[Domain] フックに切り出します。これにより、コンポーネントは表示に専念し、フックが状態とデータフローを担います。リファクタリングするのは、ユーザーが指定したコンポーネントです。同じ問題を抱える兄弟コンポーネントや親コンポーネントは、同じ変更で手を加えるファイルではなく、後続対応として報告してください。切り出しの基準と合成パターンについては、reference/REFACTORING.md を参照してください。

注意点

  • 早期リターンは、すべてのフック呼び出しの後に置く必要があります。 Section 6がSection 5の後にあるのは、単なる整頓上の理由ではありません。上にあるreturnによってレンダー間でフックの数が変わると、Reactは「Rendered fewer hooks than expected.」というエラーをスローします。ガードで高コストな処理を短絡させたい場合は、フックの内部で条件分岐してください。
  • Server ComponentsではSection 2と5を使用できません。 Next.js App Routerでは、ファイルはデフォルトでServer Componentです。useState、useEffect、styled-componentsを使うには、ファイルの先頭に'use client'が必要です。ビルドエラーにはフックの名前が示されますが、ディレクティブがないことは示されません。
  • 依存配列内のオブジェクトや配列リテラルは、無限ループを引き起こします。 }, [{ id }])や}, [items.filter(...)])はレンダーごとに新しい参照を生成するため、エフェクトが毎回再実行されます。リテラルをエフェクト内に移動するか、useMemoでメモ化してください。
  • keyによるリセットは、サブツリー全体を再マウントします。 子の状態、非制御入力の値、フォーカス、実行中のアニメーションが失われます。「これは別のレコードになった」という場合には適切ですが、値を再設定する必要があるフィールドが1つだけの場合には適しません。
  • 計測して必要だと分かるまでは、メモ化より導出を優先してください。 useMemoを使うのは、計算が本当に高コストな場合に限ってください。テンプレート文字列や短いリストに対する.map()は、メモ化の管理処理よりも低コストです。
  • 戻り値の型は、単なるJSX.ElementではなくReact.JSX.Elementです。 React 19の型定義ではグローバルなJSX名前空間が削除されたため、修飾子のない形式は解決できなくなりました。
  • @tanstack/react-query v5では、引数は1つのオブジェクトです。 位置引数を使うuseQuery(key, fn, options)のオーバーロードは削除されました。また、v5ではステータスの'loading'が'pending'に改名され、isLoadingはisPending && isFetchingとして再定義されました。そのため、「まだデータがない」状態はisPendingで表します。
  • GSDの実行フェーズでは、スキルは自動で有効になりません。 /ideal-react-componentを明示的に呼び出すか、プロジェクトのCLAUDE.mdに規約を記録してください。

詳細リファレンス

質問に応じてファイルを1つ読み込んでください。最初から読み込む必要はありません。

読み込むファイル用途
reference/SECTIONS.mdセクションごとの詳細な✅/❌、スタイリング方法のバリエーション、構造上のトラブルシューティング
reference/HOOKS-ANTIPATTERNS.md仕組みを含むアンチパターン全般。上記の代表的な3つは要約です
reference/REFACTORING.mdカスタムフックの抽出と組み合わせ
reference/COMPLETE-EXAMPLES.md7つのセクションをすべて含む、TSとJSの完全なコンポーネント例
reference/EVAL.md有効化評価セット(メンテナー専用)

出典

コメント