mcpField

react

github.com/squirrelfishcityhall150開発35 回インストール
リポジトリ

react について

React アプリを開発する方向けのリファレンススキルです。React 19、フック、Suspense、遅延読み込み、TypeScript、パフォーマンスのパターンに加え、コンポーネント設計やよくあるミスも扱います。

主な機能

  1. 01React 19のコンポーネントパターン
  2. 02フックと状態管理の指針
  3. 03Suspenseと遅延読み込み
  4. 04TypeScriptのコンポーネントパターン
  5. 05パフォーマンス最適化のヒント
  6. 06よくあるミスとファイル構成

活用シーン

  1. 01型付きReactコンポーネントの作成
  2. 02重い機能のコード分割
  3. 03Suspenseによる非同期UIの処理
  4. 04フックと描画パフォーマンスの確認

インストール

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

$npx skills add https://github.com/squirrelfishcityhall150/claude-code-kit --skill react

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

同梱ファイル

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

skill-rules-fragment.json
resources/
component-patterns.md
hooks-patterns.md
performance.md
typescript-patterns.md

詳細

React Core Patterns

目的

フック、Suspense、遅延読み込み、TypeScript を使用してモダンなアプリケーションを構築するために欠かせない React 19 のパターンです。

注意:React 19(2024 年 12 月リリース)における破壊的変更:

  • forwardRef は不要になりました。ref をプロパティとして直接渡します
  • propTypes は削除されました(無視されます)
  • 新しい JSX トランスフォームが必須です
  • React.FC 型は非推奨です。代わりに通常の関数コンポーネントを使用します

このスキルを使用する場面

  • React コンポーネントを作成するとき
  • React フック(useState、useEffect、useCallback、useMemo)を使用するとき
  • 遅延読み込みとコード分割を実装するとき
  • Suspense 境界を扱うとき
  • React 固有の TypeScript パターンを使用するとき
  • React でパフォーマンスを最適化するとき

クイックスタート

コンポーネント構造のテンプレート
import { useState, useCallback } from 'react';

interface Props {
  userId: string;
  onUpdate?: (data: UserData) => void;
}

interface UserData {
  name: string;
  email: string;
}

function UserProfile({ userId, onUpdate }: Props) {
  const [data, setData] = useState<UserData | null>(null);

  const handleUpdate = useCallback((newData: UserData) => {
    setData(newData);
    onUpdate?.(newData);
  }, [onUpdate]);

  return (
    <div>
      {/* Component content */}
    </div>
  );
}

export default UserProfile;
コンポーネントのチェックリスト

React コンポーネントを作成する場合は、次の手順に従ってください。

  • 型付きプロパティを使った関数コンポーネントを使用します(React.FC は使いません)
  • Props とローカル状態のインターフェースを定義します
  • 子コンポーネントに渡すイベントハンドラーには useCallback を使用します
  • 計算コストの高い処理には useMemo を使用します
  • 重いコンポーネントは遅延読み込みします:lazy(() => import())
  • 遅延読み込みするコンポーネントをフォールバック付きの <Suspense> で囲みます
  • ファイルの末尾でデフォルトエクスポートします
  • 条件付きフックは使用しません(フックは常に同じ順序で呼び出す必要があります)
  • ref はプロパティとして渡します(React 19 では forwardRef は不要です)

コアフックのパターン

useState
// Simple state
const [count, setCount] = useState<number>(0);

// Object state
const [user, setUser] = useState<User | null>(null);

// Array state
const [items, setItems] = useState<Item[]>([]);

// Functional updates when depending on previous state
setCount(prev => prev + 1);
setItems(prev => [...prev, newItem]);
useCallback
// Wrap functions passed to child components
const handleClick = useCallback((id: string) => {
  console.log('Clicked:', id);
}, []); // Empty deps if no dependencies

// With dependencies
const handleUpdate = useCallback((data: FormData) => {
  apiCall(userId, data);
}, [userId]); // Re-create when userId changes
useMemo
// Expensive computation
const sortedItems = useMemo(() => {
  return items.sort((a, b) => a.score - b.score);
}, [items]);

// Derived state
const totalPrice = useMemo(() => {
  return cart.reduce((sum, item) => sum + item.price, 0);
}, [cart]);
useEffect
// Run once on mount
useEffect(() => {
  fetchData();
}, []);

// Run when dependency changes
useEffect(() => {
  if (userId) {
    loadUserData(userId);
  }
}, [userId]);

// Cleanup
useEffect(() => {
  const subscription = subscribe(userId);
  return () => subscription.unsubscribe();
}, [userId]);

遅延読み込みとコード分割

基本的な遅延読み込み
import React, { Suspense } from 'react';

// Lazy load heavy component
const HeavyChart = React.lazy(() => import('./HeavyChart'));

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Suspense fallback={<div>Loading chart...</div>}>
        <HeavyChart />
      </Suspense>
    </div>
  );
}
複数コンポーネントの遅延読み込み
const AdminPanel = React.lazy(() => import('./AdminPanel'));
const UserSettings = React.lazy(() => import('./UserSettings'));
const Reports = React.lazy(() => import('./Reports'));

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <Routes>
        <Route path="/admin" element={<AdminPanel />} />
        <Route path="/settings" element={<UserSettings />} />
        <Route path="/reports" element={<Reports />} />
      </Routes>
    </Suspense>
  );
}
機能ごとのコード分割
// features/auth/index.tsx
export { default } from './AuthFeature';

// Lazy load entire feature
const AuthFeature = React.lazy(() => import('~/features/auth'));

<Suspense fallback={<FeatureLoader />}>
  <AuthFeature />
</Suspense>

Suspense のパターン

Suspense 境界
// Wrap data-fetching components
<Suspense fallback={<Skeleton />}>
  <UserProfile userId={id} />
</Suspense>

// Nested Suspense for granular loading
<Suspense fallback={<PageLoader />}>
  <Header />
  <Suspense fallback={<ContentSkeleton />}>
    <MainContent />
  </Suspense>
  <Footer />
</Suspense>
Suspense を使ったエラー境界
import { ErrorBoundary } from 'react-error-boundary';

<ErrorBoundary fallback={<ErrorFallback />}>
  <Suspense fallback={<Loading />}>
    <DataComponent />
  </Suspense>
</ErrorBoundary>

TypeScript のパターン

コンポーネントのプロパティ
// Basic props
interface ButtonProps {
  label: string;
  onClick: () => void;
  disabled?: boolean;
}

// Props with children
interface CardProps {
  title: string;
  children: React.ReactNode;
}

// Props with specific child types
interface ListProps {
  children: React.ReactElement<ItemProps> | React.ReactElement<ItemProps>[];
}

// Props with event handlers
interface FormProps {
  onSubmit: (data: FormData) => void;
  onChange?: (field: string, value: unknown) => void;
}
TypeScript でのフック
// useState with type
const [user, setUser] = useState<User | null>(null);
const [items, setItems] = useState<Item[]>([]);

// useRef with type
const inputRef = useRef<HTMLInputElement>(null);
const timerRef = useRef<number | null>(null);

// Custom hook with return type
function useUser(id: string): { user: User | null; loading: boolean } {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  // ... implementation

  return { user, loading };
}

パフォーマンスの最適化

React.memo
// Memoize component to prevent unnecessary re-renders
const UserCard = React.memo<UserCardProps>(({ user, onUpdate }) => {
  return (
    <div>
      <h3>{user.name}</h3>
      <button onClick={() => onUpdate(user.id)}>Update</button>
    </div>
  );
});

// Custom comparison function
const UserCard = React.memo(UserCardComponent, (prevProps, nextProps) => {
  return prevProps.user.id === nextProps.user.id;
});
再レンダリングを避ける
// ❌ Bad: Creates new function on every render
function Parent() {
  return <Child onClick={() => console.log('clicked')} />;
}

// ✅ Good: Stable function reference
function Parent() {
  const handleClick = useCallback(() => {
    console.log('clicked');
  }, []);

  return <Child onClick={handleClick} />;
}

よく使われるパターン

条件付きレンダリング
// Ternary operator
{isLoading ? <Spinner /> : <Content />}

// Logical AND
{error && <ErrorMessage error={error} />}

// Nullish coalescing
{user ?? <GuestView />}

// Early return for loading states
function Component() {
  const { data } = useSomeHook();

  // ❌ Avoid early returns for loading - breaks hooks rules
  // Use Suspense instead

  return <div>{data.map(...)}</div>;
}
リストとキー
// Always use stable keys
{items.map(item => (
  <ItemCard key={item.id} item={item} />
))}

// Never use index as key if list can reorder
// ❌ Bad
{items.map((item, index) => (
  <ItemCard key={index} item={item} />
))}

ファイル構成

機能ごとの構成
src/
├── features/
│   ├── auth/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── types/
│   │   └── index.tsx
│   └── posts/
│       ├── components/
│       ├── hooks/
│       ├── types/
│       └── index.tsx
├── components/  # Shared components
├── hooks/       # Shared hooks
└── types/       # Shared types
コンポーネントの近接配置
features/posts/
├── components/
│   ├── PostCard.tsx
│   ├── PostList.tsx
│   └── PostForm.tsx
├── hooks/
│   ├── usePost.ts
│   └── usePosts.ts
├── types/
│   └── post.ts
└── index.tsx  # Public API

避けるべきよくある間違い

1. 条件付きフック
// ❌ Never do this
function Component({ condition }) {
  if (condition) {
    const [state, setState] = useState(0); // Breaks rules of hooks
  }
}

// ✅ Do this
function Component({ condition }) {
  const [state, setState] = useState(0);
  // Use state conditionally, not the hook
}
2. 依存関係の指定漏れ
// ❌ Bad: Missing dependency
useEffect(() => {
  fetchUser(userId);
}, []); // userId should be in deps

// ✅ Good: All dependencies listed
useEffect(() => {
  fetchUser(userId);
}, [userId]);
3. 状態の変更
// ❌ Bad: Mutating state directly
const handleAdd = () => {
  items.push(newItem); // Don't mutate
  setItems(items);
};

// ✅ Good: Create new array
const handleAdd = () => {
  setItems([...items, newItem]);
};

参考リソース

より詳しいパターンについては、以下を参照してください。

コメント