mcpField
W
web-framework-react

web-framework-react

github.com/agents-inc開発26100 回インストール2 日前に更新
リポジトリ

web-framework-react について

React 開発者向けのコンポーネントとフックのパターンを扱うスキルで、React 19 を中心に解説します。フォーム、ref、楽観的更新、エラーバウンダリーに加え、ブラウザーとサーバー描画向けのスタイル非依存 API を扱います。

主な機能

  1. 01React 19 の ref プロパティ化とクリーンアップ
  2. 02useActionState と useFormStatus によるフォーム状態管理
  3. 03楽観的更新と Promise の読み取り
  4. 04再利用可能なコンポーネント、バリアント、カスタムフック
  5. 05エラーバウンダリーと実行時の注意点

活用シーン

  1. 01forwardRef から ref プロパティへのコンポーネント移行
  2. 02保留状態とエラー状態を持つフォームの構築
  3. 03楽観的 UI 更新と再試行可能なエラーバウンダリーの追加

インストール

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

$npx skills add https://github.com/agents-inc/skills --skill web-framework-react

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

同梱ファイル

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

metadata.yaml
reference.md
examples/
core.md
hooks.md
react-19-hooks.md

詳細

React パターン

クイックガイド: React 19 では、このスキルが有効にする 4 つの事項が確定しています。ref は通常の props であり、forwardRef は不要です。フォームの送信状態には useActionState を使用します。useFormStatus は子コンポーネントからのみ、親フォームの状態を取得できます。また、ref コールバックはクリーンアップ関数を返せます。コンポーネントは className を公開し、バリアントを data-* 属性で表現することで、スタイルに依存しない状態を保ちます。

詳細なリソース:

  • examples/core.md — コンポーネントの構成、バリアント props、イベントハンドラー、アクセシブルなコントロール
  • examples/hooks.md — usePagination、useDebounce、useLocalStorage
  • examples/react-19-hooks.md — useActionState、useFormStatus、useOptimistic、use()、ref のクリーンアップ
  • reference.md — 判断フローチャート、修正コード付きのアンチパターン、コンポーネントとフックのチェックリスト

該当するパス

  • ブラウザーで React をレンダリングする場合 — 以下のすべてのパターンがそのまま適用され、フォームの送信には examples/react-19-hooks.md の useActionState を使用します。
  • サーバーフレームワークで React をレンダリングする場合 — 送信とデータ読み込みには、そのフレームワーク独自のサーバープリミティブを使用するため、パターン 6 は省略し、コンポーネント、フック、ref、エラーバウンダリーのパターンはそのまま適用します。

<critical_requirements>

React コードを書く前に

ref は通常の props として渡します。 React 19 は forwardRef なしで ref を転送するため、ラッパーと手動の displayName が不要になります。

再利用可能なすべてのコンポーネントで className を公開します。 このスキルが把握していないコンポーネントのスタイルを、利用側が指定するための接点はこれだけです。

useFormStatus は <form> の内側にレンダリングされるコンポーネントから呼び出します。 フォームをレンダリングするコンポーネント自体で呼び出すと、エラーも表示されず、pending: false がずっと返されます。

フォームに保留中またはエラーの状態が必要な場合は、useActionState を使います。 どちらの状態も扱え、<form action={...}> はハイドレーション前でも機能します。

</critical_requirements>


自動検出: React 19、useActionState、useFormStatus、useOptimistic、use()、props としての ref、ref のクリーンアップ、forwardRef の移行、React.ComponentProps、getDerivedStateFromError、componentDidCatch、useCallback、カスタムフック

適用対象:

  • コンポーネントの props、合成、バリアント API
  • forwardRef コンポーネントから ref-as-prop への移行
  • React 19 のフックを使ったフォーム送信、楽観的更新、Promise の読み取り
  • 状態を再利用可能なロジックとして扱うカスタムフック
  • リトライ機能付きエラーバウンダリー

対象外:

  • スタイリング — コンポーネントは className を受け取り、バリアントを data-* として公開します。それらを埋める CSS の手法は対象外です
  • コンポーネントの存続期間を超えるクライアント状態、およびキャッシュと無効化を伴うサーバーデータ — コンポーネントは両方を props として受け取り、その取得元を意識しません
  • ルーティング、およびルートが読み込むデータ
  • ネットワークのテスト用ダブル

コアパターン

パターン 1:コンポーネントの構成

再利用可能なコンポーネントは、要素自身の props を展開し、ref を直接受け取り、どのスタイリング層からも指定できる data-* 属性でバリアントを表現します。

export type ButtonProps = React.ComponentProps<"button"> & {
  variant?: "default" | "ghost" | "link";
  size?: "default" | "large" | "icon";
  ref?: React.Ref<HTMLButtonElement>;
};

export function Button({ variant = "default", size = "default", className, ref, ...props }: ButtonProps) {
  return <button className={className} data-variant={variant} data-size={size} ref={ref} {...props} />;
}

コード全文:examples/core.md


パターン 2:型付きバリアント props

視覚的な軸が 2 つ以上あるコンポーネントには、バリアント props を用意します。視覚スタイルが 1 つのコンポーネントが受け取るのは className のみです。メンバーが 1 つだけのユニオンは、2 つ目のケースを持たない抽象化です。

export type AlertVariant = "info" | "warning" | "error" | "success";

export function Alert({ variant = "info", className, ref, ...props }: AlertProps) {
  return <div ref={ref} className={className} data-variant={variant} {...props} />;
}

コード全文:examples/core.md


パターン 3:イベントハンドラーの命名

内部ハンドラーには handle を付け(handleNameChange)、コールバック prop には on を付けます(onSelect)。また、各ハンドラーでイベント型(FormEvent<HTMLFormElement>、ChangeEvent<HTMLInputElement>)を指定することで、誤ったフィールドへのアクセスをコンパイル時に検出できます。

コード全文:examples/core.md


パターン 4:カスタムフック

フックを呼び出し、何もレンダリングしないロジックは、コンポーネントではなく、use を先頭に付けたフックにします。ページネーションの状態、デバウンスされた値、永続化された設定などが該当します。

コード全体:examples/hooks.md


パターン 5:再試行に対応したエラーバウンダリ

エラーバウンダリはレンダリングエラーを捕捉し、リセット関数を返すため、一時的な障害の影響はページ全体ではなく、1 つのセクションにとどまります。ルートだけでなく、各機能領域を囲むように配置します。

interface Props {
  children: ReactNode;
  fallback?: (error: Error, reset: () => void) => ReactNode;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
}

getDerivedStateFromError にはフック形式がないため、エラーバウンダリはクラスです。配置、復旧、フォールバックの設計はそれぞれ独立したテーマであり、このスキルの範囲外で扱います。


パターン 6:フォーム送信に useActionState を使用する

このフックは、アクションの直近の結果、<form action={...}> に渡すアクション、保留状態を示すフラグを返します。これにより、3 つの useState 呼び出しと、それらのリセット処理を置き換えられます。

async function updateProfile(prevState: string | null, formData: FormData) {
  try {
    await saveProfile({ name: formData.get("name") as string });
    return null;
  } catch {
    return "Failed to save profile";
  }
}

const [error, submitAction, isPending] = useActionState(updateProfile, null);

コード全体:examples/react-19-hooks.md


パターン 7:送信ボタンに useFormStatus を使用する

送信ボタンは、親フォームの保留状態を自分で読み取るため、1 つのボタンコンポーネントをすべてのフォームで使えます。また、フォームからフラグを渡す必要もありません。

import { useFormStatus } from "react-dom";

function SubmitButton({ children }: { children: React.ReactNode }) {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending} aria-busy={pending}>
      {pending ? "Submitting..." : children}
    </button>
  );
}

コード全体:examples/react-19-hooks.md


パターン 8:即時のフィードバックに useOptimistic を使用する

期待される結果をすぐにレンダリングし、リクエストが失敗した場合は React に元に戻させます。セッターは startTransition の中で呼び出します。

const [optimisticItems, addOptimistic] = useOptimistic(
  items,
  (state, update: Item) => [...state, { ...update, pending: true }],
);

startTransition(async () => {
  addOptimistic(newItem);
  await saveItem(newItem);
});

コード全体:examples/react-19-hooks.md


パターン 9:プロミスとコンテキストに use() を使用する

use() はレンダリング中にプロミスまたはコンテキストを読み取り、useContext とは異なり、早期リターンの後でも呼び出せます。プロミスは解決するまでサスペンドするため、呼び出し元を <Suspense> の内側に置き、拒否された場合はエラーバウンダリで捕捉します。

function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
  const comments = use(commentsPromise);
  return <ul>{comments.map((c) => <li key={c.id}>{c.text}</li>)}</ul>;
}

<Suspense fallback={<p>Loading...</p>}>
  <Comments commentsPromise={fetchComments()} />
</Suspense>

コード全体:examples/react-19-hooks.md


パターン 10:ref コールバックのクリーンアップ

ref コールバックはクリーンアップ関数を返すことができ、アンマウント時に実行されます。これにより、DOM のセットアップに使う useRef と useEffect の組み合わせを置き換えられます。

<video
  ref={(video) => {
    if (!video) return;
    video.play();
    return () => {
      video.pause();
      video.currentTime = 0;
    };
  }}
  src={src}
/>

コード全体:examples/react-19-hooks.md


<red_flags>

要注意事項

実行時に問題が起きます:

  • <form> をレンダリングするコンポーネントで useFormStatus を呼び出すと、pending は false のままでボタンが無効になりません。子コンポーネントから呼び出してください
  • try/catch の中で use() を呼び出すと、サスペンドのために例外が投げられ、catch によってサスペンドが握りつぶされます。代わりに呼び出し元をエラーバウンダリで囲んでください
  • トランジションまたはフォームアクションの外で useOptimistic のセッターを呼び出すと、React によって呼び出しが拒否され、楽観的な値が反映されません
  • SSR のレンダリング中にブラウザー API を読み取る場合は、typeof window !== "undefined" で保護するか、読み取りをエフェクト内に移動してください
  • イベントハンドラー、非同期処理、SSR のエラーをエラーバウンダリで捕捉しようとしても、捕捉できるのはレンダリングエラーだけです。そのため、これらの処理経路では独自に try/catch を実装してください

予想外の動作:

  • React 19 では、クリーンアップ関数以外を返す ref コールバックは型エラーになります。また、アンマウント時にコールバックへ null が渡されることもなくなりました
  • useOptimistic は失敗時に自動で元に戻すため、手動でロールバックすると競合します
  • メモ化されていない子コンポーネントに渡すハンドラーを useCallback で囲んでも、メモリー割り当てのコストがかかるだけで効果はありません。どちらにしても React Compiler により、そのラッパーは不要になります
  • forwardRef は引き続きコンパイルされるため、ラッパーとその displayName が無駄になっても警告されません
  • className を持たないコンポーネントは、利用側でスタイルを設定できません

</red_flags>

コメント