mcpField
R
react-advanced

react-advanced

github.com/trancong12102開発749 回インストール6 週間前に更新
リポジトリ

react-advanced について

TanStack エコシステムと XState を使うモダンな React アプリ向けの、クロスプラットフォームな規約と設計ガイドです。React Web と React Native の開発者を対象に、サーバーデータ、UI 状態、フォーム、非同期処理の役割分担やよくある問題を説明します。

主な機能

  1. 01サーバー状態とクライアント状態の分離
  2. 02用途別のライブラリ責務の定義
  3. 03XState を使う判断基準の提示
  4. 04コンポーネント合成パターンの説明
  5. 05テーマ別リファレンスファイルの提供

活用シーン

  1. 01React アプリの状態管理の設計
  2. 02複数段階の非同期フローの設計
  3. 03useEffect での派生状態処理の回避
  4. 04フォームとサーバーデータの管理方法の選択

インストール

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

$npx skills add https://github.com/trancong12102/agentskills --skill react-advanced

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

同梱ファイル

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

references/
form.md
integration.md
react-query.md
table.md
testing.md
xstate.md
zod.md
zustand.md

詳細

React Advanced:コアパターンと規約(クロスプラットフォーム)

このスキルでは、TanStack エコシステムと XState を使用して最新の React アプリケーションを構築する際のルール、規約、アーキテクチャ上の決定を定めます。よくある落とし穴を防ぎ、拡張性のあるパターンを徹底するため、意図的に明確な方針を採用しています。

これらのパターンは、Web と React Native のどちらでも同じように機能します。プラットフォーム固有のパターンについては、以下を参照してください。

  • Web: react-web-advanced を参照してください(TanStack Router、Start、Virtual)
  • React Native: react-native-advanced を参照してください(Expo Router、FlashList、MMKV)

ここで言及するライブラリの詳しい API ドキュメントについては、適切な他のツール (ドキュメントの検索、ウェブ検索など)を使用してください。このスキルでは、これらのツールの完全な API 仕様ではなく、使い方と使う理由に焦点を当てています。

useEffect の禁止

以下の用途に useEffect を使用しないでください。

  • データ取得 — React Query(useSuspenseQuery / useQuery)またはルートローダーを使用してください
  • 派生状態 — レンダリング中に計算するか、useMemo を使用してください
  • props との状態の同期 — prop を直接使用するか、React の key を使ってリセットしてください
  • ユーザーイベントへの応答 — イベントハンドラーにロジックを記述してください
  • 外部ストアの購読 — useSyncExternalStore を使用してください
  • 複雑な非同期処理 — invoke / fromPromise を使用する XState マシンを使ってください
useEffect の許容される用途

正当なケース:

  1. React 以外の外部システムとの同期 — DOM API、サードパーティ製ウィジェット (マップ、チャート)、マウント/アンマウントのライフサイクルが必要な命令型ライブラリ
  2. クリーンアップを伴うブラウザー/ネイティブ API の購読 — 単発のケースで useSyncExternalStore の抽象度が低すぎる場合(WebSocket 接続、リサイズオブザーバー)
  3. マウント時の分析/ログ記録 — 状態を更新しない、実行してそのまま完了する副作用
  4. React Query のデータを XState に橋渡しする — イベントを介してサーバー状態をマシンに 送るための useEffect ブリッジパターン(references/xstate.md を参照)
マウント時のみの副作用に使う useMountEffect

正当なマウント時のみの副作用(上記のケース 1〜3)がある場合は、生の useEffect(fn, []) の代わりに useMountEffect ヘルパーを使用してください。

// utils/useMountEffect.ts
import { useEffect, type EffectCallback } from "react";

// eslint-disable-next-line react-hooks/exhaustive-deps
const useMountEffect = (effect: EffectCallback) => useEffect(effect, []);

export default useMountEffect;

使用例:

useMountEffect(() => {
  const plugin = $.myPlugin(ref.current);
  return () => {
    plugin.destroy();
  };
});

理由:生の useEffect(fn, []) は react-hooks/exhaustive-deps リントルールに抵触し、 空の配列が意図的であることを開発者に証明させます。useMountEffect を使えば、「マウント時に一度だけ実行する」という意図がコード上で明確になり、警告も正しく抑制されます。

代わりに使うもの
useEffect の代わりに使う用途使用するもの
データ取得useSuspenseQuery + ルートローダーでの先行取得
Promise の処理use() フック(React 19 以降)+ <Suspense>
派生値/計算値直接計算または useMemo
外部ストアの購読useSyncExternalStore
負荷の高いレンダリングの遅延useDeferredValue / useTransition
複雑な非同期オーケストレーションfromPromise を使う XState の invoke
prop の変更時に状態をリセットコンポーネントの React key prop
ユーザーが起動する副作用イベントハンドラーに直接記述

状態管理の考え方

サーバー状態とクライアント状態 — 決して混在させない

サーバー状態(API/データベースから取得するデータ)とクライアント状態(クライアント内にのみ存在する UI 状態)は、根本的に異なる関心事です。混在させると、データの陳腐化によるバグ、重複、同期に関する深刻な問題が発生します。

関心事所有者例
サーバーデータReact Queryユーザー、投稿、製品、注文
URL/ルート状態Routerパスパラメーター、検索パラメーター(TanStack Router または Expo Router)
複雑な UI フローXState複数ステップのウィザード、認証フロー、ドラッグ&ドロップ
共有クライアント UIZustandテーマ、サイドバー、選択項目、グローバルフィルター、設定
フォームフィールドTanStack Form入力値、検証エラー、送信
スキーマ検証Zod検索パラメーター、フォームバリデーター、API コントラクト
シンプルなローカル UIuseStateトグル、アコーディオンの展開状態、入力フォーカス
判断フローチャート
Is the data from a server / API?
  YES -> React Query (queryOptions + useSuspenseQuery)
  NO -> Is it in the URL / route params?
    YES -> Router (platform-specific: TanStack Router or Expo Router)
    NO -> Is it a complex multi-state flow (3+ states, async, guards)?
      YES -> XState (useMachine / createActorContext)
      NO -> Is it a form field?
        YES -> TanStack Form (with Zod validators)
        NO -> Is it shared across components / trees?
          YES -> Zustand (create store + selectors)
          NO -> useState / useReducer
useState/useReducer より XState を選ぶ場合

次のような場合は XState を使用します。

  • 遷移が定義された、3 つ以上の相互排他的な状態がある
  • 非同期の副作用を状態変更時にキャンセルする必要がある(競合状態)
  • ロジックに、遷移を制御する条件であるガードがある
  • 独立した関心事に対して並列状態が必要である
  • React から分離してフローをテストする必要がある
  • 戻る/進むナビゲーションを含む複数ステップのフローがある(ウィザード)

シンプルなトグル、単一のブール値フラグ、カウンター状態には XState を使わないでください。これらには useState を使用します。


アーキテクチャ:各ライブラリの役割

レイヤーライブラリ責務
サーバー状態React Query取得、キャッシュ、無効化、バックグラウンドでの再取得
複雑な UI 状態XStateステートマシン、アクターモデル、フローのオーケストレーション
共有クライアント UIZustandコンポーネント間で共有する UI 状態、設定、選択項目
フォームのライフサイクルTanStack Formフィールド値、検証、送信
スキーマ検証Zod検索パラメーター、フォームバリデーター、API コントラクト
データ表示TanStack Tableヘッドレスな並べ替え、フィルタリング、ページネーション、グループ化
テストVitest + TLユニット、コンポーネント、統合、マシンのテスト
シンプルなローカル状態useStateトグル、ローカル入力、コンポーネント内の値
黄金律:queryOptions を信頼できる唯一の情報源にする

クエリオプションは一度だけ定義し、ローダー、コンポーネント、無効化処理など、あらゆる場所でインポートします。

export const postsQueryOptions = queryOptions({
  queryKey: ["posts"],
  queryFn: fetchPosts,
  staleTime: 30_000,
});

export const postQueryOptions = (postId: string) =>
  queryOptions({
    queryKey: ["posts", postId],
    queryFn: () => fetchPost(postId),
    staleTime: 30_000,
  });

コンポーネントの合成

複合コンポーネント

コンポーネントのグループが暗黙的な状態を共有する場合は、Context ベースの複合コンポーネントを使用します。親が状態を管理し、子はコンテキストを通じて状態を利用します。コンテキストの値はメモ化してください。

スロットパターン

スロットのような合成には、名前付きプロパティ(header、footer、actions)を使用します。深くネストしたレンダープロップのツリーは避けてください。

制御の反転

呼び出し元固有の動作に対応するためにブール値のプロパティや分岐ロジックを追加する場合は、コールバック、リデューサー、またはレンダー関数を通じて、そのロジックを呼び出し元に戻してください。似たような if 文が 3 つある場合は、制御を反転する合図です。

よくある落とし穴

  1. useEffect で派生状態を扱う — エフェクト内で値を計算して useState に保存すると、 再レンダリングが二重に発生します。レンダー中に計算するか、useMemo を使用してください。

  2. サーバーデータをクライアント状態に保存する — API レスポンスを Zustand/Redux に格納すると、 キャッシュと無効化を自分で管理することになります。代わりに React Query を使用してください。

  3. URL 状態を useState に重複して保存する — ルーターの検索パラメーター/パラメーターフックを直接使用してください。

  4. 同じデータに React Query と XState の両方を使用する — React Query がデータの取得とキャッシュを管理します。 XState はイベント経由でデータを受け取り、オーケストレーションのみを処理します。

  5. XState マシン内で React フックを呼び出す — フックは React コンポーネント内でのみ使用できます。 マシン内では fromPromise を使用し、useEffect のイベント経由でデータを連携してください。

  6. 配列のインデックスをキーとして使う — 安定した ID(item.id)を使用してください。インデックスをキーにすると、並べ替え/挿入/削除時に状態の関連付けが誤ります。

  7. コンポーネント内でコンポーネントを定義する — レンダーのたびに新しいコンポーネント型が作成され、 React にアンマウントと再マウントを強制します。モジュールレベルで定義してください。

  8. 更新頻度の高い状態に Context を使う — Context は変更のたびにすべてのコンシューマーを再レンダリングします。 共有され、頻繁に変化する値にはセレクターを使った Zustand を使用するか(references/zustand.md を参照)、 コンポーネント内に限定される状態にはローカル状態を使用してください。

  9. Zod の検索パラメータースキーマで .catch() を使わない — .default() が処理するのは キーがない場合のみです。.catch() は不正な URL による無効な値も処理します。


リファレンスファイル

特定のライブラリを扱う際は、関連するリファレンスファイルを読んでください。

ファイル読むタイミング
references/react-query.mdクエリのパターン、ミューテーション、キャッシュ、Suspense との連携
references/table.md列定義、並べ替え、フィルタリング、ページネーション、サーバーサイド処理
references/form.mdフィールドの検証、配列、スキーマ検証、パフォーマンス
references/xstate.mdステートマシン、アクター、認証フロー、ウィザード、React との連携
references/zustand.md共有クライアント UI 状態、セレクター、スライス、ミドルウェア、バニラストア
references/zod.mdスキーマ検証、v4 API、Form との連携、エラー処理
references/testing.mdVitest のセットアップ、Testing Library、MSW、Query/Form/XState のテスト
references/integration.mdライブラリの組み合わせ:Zustand+XState、Query+XState のブリッジ

コメント