mcpField
R
react

react

github.com/blencorp開発11197 回インストール49 週間前に更新
リポジトリ

react について

React アプリを開発する人向けのスキルで、React 19 のコンポーネント、フック、Suspense、遅延読み込み、TypeScript のパターンを解説します。パフォーマンス、構成、よくあるミスも扱います。

主な機能

  1. 01React 19 のコンポーネントパターン
  2. 02フックの使用法と依存関係
  3. 03Suspense と遅延読み込み
  4. 04コンポーネントの TypeScript パターン
  5. 05レンダリング性能の手法
  6. 06機能単位のファイル構成

活用シーン

  1. 01型付き React コンポーネントの作成
  2. 02大きな機能のコード分割
  3. 03フックや状態管理パターンの確認

インストール

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

$npx skills add https://github.com/blencorp/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]);
};

参考リソース

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

コメント