mcpField
R
react-design-patterns

react-design-patterns

github.com/b4r7x開発046 回インストール12 週間前に更新
リポジトリ

react-design-patterns について

React開発者向けに、13種類のコンポーネントやアプリケーション設計パターンを解説するスキルです。選択ガイドを備え、まずCustom Hookを試し、必要に応じて構造的なパターンへ進む方針を示します。

主な機能

  1. 01パターン選択ガイド
  2. 0213種類のReact設計パターン
  3. 03制御・非制御コンポーネントの解説
  4. 04ロジックと表示の分離
  5. 05エラー境界とPortalの解説

活用シーン

  1. 01再利用可能なロジックのパターン選定
  2. 02TabsやModalの複合コンポーネント設計
  3. 03データ取得と表示の分離
  4. 04デザインシステムの構成

インストール

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

$npx skills add https://github.com/b4r7x/agent-skills --skill react-design-patterns

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

詳細

React のデザインパターン

概要

2025年の人気度に基づいて順位付けした13のパターンです。基本原則:まずカスタムフックから始め、構造の共有が必要な場合にのみ複合コンポーネントに移行します。

パターンの選択ガイド

必要なものパターン
UI に関する意見を含まない、再利用可能なロジックカスタムフック
制御コンポーネントと非制御コンポーネントの動作制御プロップ
密接に連携する UI サブコンポーネント(タブ、モーダル、アコーディオン)複合コンポーネント
スタイルを強制しないロジックヘッドレスコンポーネント
データ取得とレンダリングの分離コンテナー/プレゼンテーショナル
グローバルで安定した状態(認証、テーマ)プロバイダー(Context)
データフローの逆転(子 → 親)レンダープロップ
children による完全なレンダリング制御関数としての children
事前構築済みプロップを使った柔軟な APIプロップゲッター
クラッシュの分離エラーバウンダリー
DOM の親の外側へのレンダリング(モーダル、ツールチップ)ポータル
デザインシステムの階層構造アトミックデザイン

1. カスタムフック ⭐⭐⭐⭐⭐

状態を持つロジックを再利用可能な関数に切り出します。2025年に最も人気のあるパターンです。

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    try { return JSON.parse(localStorage.getItem(key)) ?? initialValue; }
    catch { return initialValue; }
  });
  const set = useCallback((v) => {
    setValue(prev => {
      const next = v instanceof Function ? v(prev) : v;
      localStorage.setItem(key, JSON.stringify(next));
      return next;
    });
  }, [key]);
  return [value, set];
}

2. 制御プロップ ⭐⭐⭐⭐

コンポーネントは、制御モード(親がプロップを介して状態を所有)と非制御モード(コンポーネント自身が状態を管理)の両方に対応します。すべてのフォーム入力とコンポーネントライブラリで使われています。

// Controlled — parent manages state
<EditableTitle title={title} onChange={setTitle} />

// Uncontrolled — component manages own state, parent can reset via key
<EditableTitle key={userId} defaultTitle="Untitled" />

// Implementation supporting both modes
function EditableTitle({ title, defaultTitle = '', onChange }) {
  const [internal, setInternal] = useState(defaultTitle);
  const isControlled = title !== undefined;
  const value = isControlled ? title : internal;

  const handleChange = (e) => {
    if (!isControlled) setInternal(e.target.value);
    onChange?.(e.target.value);
  };

  return <input value={value} onChange={handleChange} />;
}

3. 複合コンポーネント ⭐⭐⭐⭐

ローカルコンテキストを介して状態を共有するサブコンポーネントの一群です。Radix UI、Headless UI、React Aria で使われています。

const TabsContext = createContext(null);
function Tabs({ children, defaultTab }) { /* owns [activeTab, setActiveTab], provides both via context */ }
Tabs.Tab = /* reads context, renders the trigger button */
Tabs.Panel = /* reads context, renders children when active */

構成:親が状態を所有し、ローカルコンテキストを介して提供します。ドットで名前空間を区切ったサブコンポーネントが状態を利用します。useTabs() ガードフックは、サブコンポーネントが <Tabs> の外でレンダリングされるとエラーをスローします。実行可能な完全版の実装は react-usecontext スキルにあります(この例の唯一の管理元です)。複合コンポーネントを作成するときに読み込んでください。

4. ヘッドレスコンポーネント ⭐⭐⭐⭐

フックはロジックのみを提供し、HTML も CSS も一切含みません。UI は完全に自由に作成できます。

function useAccordion() {
  const [openIndex, setOpenIndex] = useState(null);
  const toggle = useCallback((i) => setOpenIndex(prev => prev === i ? null : i), []);
  const isOpen = useCallback((i) => openIndex === i, [openIndex]);
  return { toggle, isOpen };
}
// Two completely different UIs can use the same hook

5. コンテナー/プレゼンテーショナル ⭐⭐⭐

コンテナー(ロジック、データ取得、ナビゲーション)とプレゼンテーショナル(プロップを介してデータを受け取り、レンダリングのみを行う)に分割します。

// Presentational — pure UI, easy to test
const UserCard = ({ user, onFollow }) => (
  <div><h2>{user.name}</h2><button onClick={onFollow}>Follow</button></div>
);
// Container — logic and data
const UserCardContainer = ({ userId }) => {
  const { user, follow } = useUser(userId);
  if (!user) return <Skeleton />;
  return <UserCard user={user} onFollow={follow} />;
};

6. プロバイダー(Context) ⭐⭐⭐⭐

グローバル/ローカル状態には、createContext と useContext を使います。詳しくは react-usecontext スキルを参照してください。

7. レンダープロップ ⭐⭐

プロップとして関数を渡し、コンポーネントがデータを渡してその関数を呼び出します。

<WindowSize render={({ width }) => (
  width < 768 ? <MobileMenu /> : <DesktopMenu />
)} />

データフローを逆転させる場合(子 → 親)に便利です。2025年には、ほとんどがカスタムフックに置き換えられています。

8. 関数としての children ⭐⭐

レンダープロップの派生形で、関数を children として渡します。Formik、Downshift によって広まりました。

<Toggle>
  {({ on, toggle }) => (
    <button onClick={toggle}>{on ? 'ON' : 'OFF'}</button>
  )}
</Toggle>

9. プロップゲッター ⭐⭐

フックが、要素に展開するための事前構築済みプロップオブジェクトを返します。React Hook Form(register)、react-table で使われています。

function useToggle() {
  const [on, setOn] = useState(false);
  const getTogglerProps = (overrides = {}) => ({
    onClick: () => setOn(prev => !prev),
    'aria-pressed': on,
    ...overrides,
  });
  return { on, getTogglerProps };
}
// Usage: <button {...getTogglerProps({ className: 'my-btn' })}>

10. エラーバウンダリー ⭐⭐

コンポーネントツリー内の JS エラーを捕捉し、フォールバック UI を表示します。クラスコンポーネントまたは react-error-boundary ライブラリが必要です。

import { ErrorBoundary } from 'react-error-boundary';
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => { /* reset state */ }}>
  <Dashboard />
</ErrorBoundary>

十分に活用されていません。ほとんどのアプリでは、セクションごとに細かくエラーバウンダリーを設けるべきです。

11. ポータル ⭐⭐⭐

DOM の親ツリーの外側にレンダリングします。z-index、overflow: hidden、クリッピングの問題を解決します。

import { createPortal } from 'react-dom';
function Modal({ isOpen, children }) {
  if (!isOpen) return null;
  return createPortal(
    <div className="modal-overlay">{children}</div>,
    document.body
  );
}

12. HOC(高階コンポーネント) ⭐ — レガシー

コンポーネントを受け取り、新しいコンポーネントを返す関数です。現代のコードではカスタムフックに置き換えられています。

// Legacy
const ProtectedDashboard = withAuth(Dashboard);
// Modern equivalent
function ProtectedRoute({ children }) {
  const { user } = useAuth();
  if (!user) return <Navigate to="/login" />;
  return children;
}

13. アトミックデザイン ⭐⭐⭐

階層構造:Atoms(Button)→ Molecules(SearchBox = Input + Button)→ Organisms(Header)→ Templates(データを含まないレイアウト)→ Pages(データを含むテンプレート)。デザインシステムや大規模チームのプロジェクトに最適です。

参考資料

コメント