compound-pattern
タブ、アコーディオン、ドロップダウンなど、明示的なpropsの受け渡しなしに連携する必要がある関連コンポーネントを構築する際に活用できる、暗黙の共有状態を持つコンパウンドコンポーネントパターンを解説するスキルです。
description の原文を見る
Teaches the compound component pattern for shared implicit state. Use when building related components like tabs, accordions, or dropdowns that need to coordinate without explicit prop passing.
SKILL.md 本文
複合コンポーネントパターン
目次
アプリケーション内には、相互に関連するコンポーネントがしばしば存在します。これらは共有された状態を通じて相互に依存し、ロジックも一緒に共有しています。select ドロップダウンコンポーネント、メニューアイテムなどでよく見かけます。複合コンポーネントパターンを使うことで、一緒に動作してタスクを遂行するコンポーネントを作成できます。
使用すべき場合
- ドロップダウン、タブ、メニューなど、関連するサブコンポーネントを持つコンポーネントを構築する場合
- 内部の状態管理を公開することなく、クリーンなコンポーネント API を提供したい場合
使用してはいけない場合
- サブコンポーネントが意味のある状態を共有していない場合 — パターンは不要な Context のオーバーヘッドを追加します
- シンプルな単発の UI で、単一コンポーネントと props の方が明確な場合
- 暗黙的な状態共有がコンポーネントの動作をユーザーにとって予測困難にする場合
手順
- 親の複合コンポーネントとその子コンポーネント間の状態共有に React Context API を使用する
- 子コンポーネントを親のスタティックプロパティとして設定する(例:
FlyOut.Toggle、FlyOut.List) - 複雑なシナリオでの不要な再レンダリングを避けるため、Context の値をメモ化する
- より柔軟なコンポーネントネストのために
React.Children.mapより Context アプローチを優先する
詳細
Context API
例を見てみましょう:リスの画像のリストがあります!リスの画像を表示するだけでなく、ユーザーが画像を編集または削除できるボタンを追加したいとします。ユーザーがコンポーネントをトグルしたときにリストを表示する FlyOut コンポーネントを実装できます。
FlyOut コンポーネント内には、本質的に3つのものがあります:
- トグルボタンとリストを含む
FlyOutラッパー ListをトグルするToggleボタン- メニューアイテムのリストを含む
List
React の Context API を使った複合コンポーネントパターンはこの例に最適です!
まず、FlyOut コンポーネントを作成しましょう。このコンポーネントは状態を保持し、受け取ったすべての子に toggle の値を含む FlyOutProvider を返します。
const FlyOutContext = createContext();
function FlyOut(props) {
const [open, toggle] = useState(false);
return (
<FlyOutContext.Provider value={{ open, toggle }}>
{props.children}
</FlyOutContext.Provider>
);
}
これで、open と toggle の値を子コンポーネントに渡せるステートフルな FlyOut コンポーネントができました!
Toggle コンポーネントを作成しましょう。このコンポーネントは、ユーザーがメニューをトグルするためにクリックできるコンポーネントを単にレンダリングします。
function Toggle() {
const { open, toggle } = useContext(FlyOutContext);
return (
<div onClick={() => toggle(!open)}>
<Icon />
</div>
);
}
Toggle に FlyOutContext プロバイダーへのアクセスを与えるためには、FlyOut の子コンポーネントとしてレンダリングする必要があります!また、Toggle コンポーネントを FlyOut のプロパティにすることもできます!
const FlyOutContext = createContext();
function FlyOut(props) {
const [open, toggle] = useState(false);
return (
<FlyOutContext.Provider value={{ open, toggle }}>
{props.children}
</FlyOutContext.Provider>
);
}
function Toggle() {
const { open, toggle } = useContext(FlyOutContext);
return (
<div onClick={() => toggle(!open)}>
<Icon />
</div>
);
}
FlyOut.Toggle = Toggle;
これは、任意のファイルで FlyOut コンポーネントを使いたい場合に、FlyOut をインポートするだけで済むということです!
import React from "react";
import { FlyOut } from "./FlyOut";
export default function FlyoutMenu() {
return (
<FlyOut>
<FlyOut.Toggle />
</FlyOut>
);
}
トグルだけでは不十分です。open の値に基づいて開閉するリストアイテムを含む List も必要です。
function List({ children }) {
const { open } = React.useContext(FlyOutContext);
return open && <ul>{children}</ul>;
}
function Item({ children }) {
return <li>{children}</li>;
}
List コンポーネントは、open の値が true か false かに基づいて子をレンダリングします。Toggle コンポーネントと同じように、List と Item を FlyOut コンポーネントのプロパティにしましょう。
const FlyOutContext = createContext();
function FlyOut(props) {
const [open, toggle] = useState(false);
return (
<FlyOutContext.Provider value={{ open, toggle }}>
{props.children}
</FlyOutContext.Provider>
);
}
function Toggle() {
const { open, toggle } = useContext(FlyOutContext);
return (
<div onClick={() => toggle(!open)}>
<Icon />
</div>
);
}
function List({ children }) {
const { open } = useContext(FlyOutContext);
return open && <ul>{children}</ul>;
}
function Item({ children }) {
return <li>{children}</li>;
}
FlyOut.Toggle = Toggle;
FlyOut.List = List;
FlyOut.Item = Item;
これで FlyOut コンポーネントのプロパティとして使用できます!この場合、ユーザーに2つのオプション(Edit と Delete)を表示したいとします。Edit オプション用に1つ、Delete オプション用に1つの FlyOut.Item コンポーネントをレンダリングする FlyOut.List を作成しましょう。
import React from "react";
import { FlyOut } from "./FlyOut";
export default function FlyoutMenu() {
return (
<FlyOut>
<FlyOut.Toggle />
<FlyOut.List>
<FlyOut.Item>Edit</FlyOut.Item>
<FlyOut.Item>Delete</FlyOut.Item>
</FlyOut.List>
</FlyOut>
);
}
完璧です!FlyOutMenu 自体に状態を追加することなく、完全な FlyOut コンポーネントを作成しました!
複合パターンはコンポーネントライブラリを構築する際に優れています。Semantic UI のような UI ライブラリを使用するときにこのパターンがよく見られます。
React.Children.map
複合コンポーネントパターンは、コンポーネントの子をマッピングすることでも実装できます。これらの要素を追加の props でクローンすることで、open と toggle プロパティを追加できます。
export function FlyOut(props) {
const [open, toggle] = React.useState(false);
return (
<div>
{React.Children.map(props.children, (child) =>
React.cloneElement(child, { open, toggle })
)}
</div>
);
}
すべての子コンポーネントはクローンされ、open と toggle の値が渡されます。前の例のように Context API を使う必要がなく、props を通じてこの2つの値にアクセスできます。
利点
複合コンポーネントは独自の内部状態を管理し、複数の子コンポーネント間でこれを共有します。複合コンポーネントを実装する場合、状態管理について自分たちで心配する必要がありません。
複合コンポーネントをインポートする場合、そのコンポーネントで利用可能な子コンポーネントを明示的にインポートする必要がありません。
import { FlyOut } from "./FlyOut";
export default function FlyoutMenu() {
return (
<FlyOut>
<FlyOut.Toggle />
<FlyOut.List>
<FlyOut.Item>Edit</FlyOut.Item>
<FlyOut.Item>Delete</FlyOut.Item>
</FlyOut.List>
</FlyOut>
);
}
欠点
React.Children.map を使って値を提供する場合、コンポーネントのネストに制限があります。親コンポーネントの直接の子のみが open と toggle props にアクセスでき、これらのコンポーネントを別のコンポーネントでラップすることができません。
export default function FlyoutMenu() {
return (
<FlyOut>
{/* これは機能しません */}
<div>
<FlyOut.Toggle />
<FlyOut.List>
<FlyOut.Item>Edit</FlyOut.Item>
<FlyOut.Item>Delete</FlyOut.Item>
</FlyOut.List>
</div>
</FlyOut>
);
}
React.cloneElement で要素をクローンすると浅いマージが実行されます。既存の props は、React.cloneElement メソッドに渡す新しい props とマージされます。同じ名前の既存の prop がある場合、名前の衝突が発生する可能性があります。props が浅くマージされるため、その prop の値は渡す最新の値で上書きされます。
ソース
参考資料
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- patternsdev
- リポジトリ
- patternsdev/skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/patternsdev/skills / ライセンス: MIT
関連スキル
superfluid
Superfluidプロトコルおよびそのエコシステムに関するナレッジベースです。Superfluidについて情報を検索する際は、ウェブ検索の前にこちらを参照してください。対応キーワード:Superfluid、CFA、GDA、Super App、Super Token、stream、flow rate、real-time balance、pool(member/distributor)、IDA、sentinels、liquidation、TOGA、@sfpro/sdk、semantic money、yellowpaper、whitepaper
civ-finish-quotes
実質的なタスクが真に完了した際に、文明風の儀式的な引用句を追加します。ユーザーやエージェントが機能追加、リファクタリング、分析、設計ドキュメント、プロセス改善、レポート、執筆タスクといった実際の成果物を完成させるときに、明示的な依頼がなくても使用します。短い返信や小さな修正、未完成の作業には適用しません。
nookplot
Base(Ethereum L2)上のAIエージェント向け分散型調整ネットワークです。エージェントがオンチェーンアイデンティティを登録する、コンテンツを公開する、他のエージェントにメッセージを送る、マーケットプレイスで専門家を雇う、バウンティを投稿・請求する、レピュテーションを構築する、共有プロジェクトで協業する、リサーチチャレンジを解くことでNOOKをマイニングする、キュレーションされたナレッジを備えたスタンドアロンオンチェーンエージェントをデプロイする、またはアグリーメントとリワードで収益を得る場合に利用できます。エージェントネットワーク、エージェント調整、分散型エージェント、NOOKトークン、マイニングチャレンジ、ナレッジバンドル、エージェントレピュテーション、エージェントマーケットプレイス、ERC-2771メタトランザクション、Prepare-Sign-Relay、AgentFactory、またはNookplotが言及された場合にトリガーされます。
web3-polymarket
Polygon上でのPolymarket予測市場取引統合です。認証機能(L1 EIP-712、L2 HMAC-SHA256、ビルダーヘッダー)、注文発注(GTC/GTD/FOK/FAK、バッチ、ポストオンリー、ハートビート)、市場データ(Gamma API、Data API、オーダーブック、サブグラフ)、WebSocketストリーミング(市場・ユーザー・スポーツチャネル)、CTF操作(分割、統合、償却、ネガティブリスク)、ブリッジ機能(入金、出金、マルチチェーン)、およびガスレスリレイトランザクションに対応しています。AIエージェント、自動マーケットメーカー、予測市場UI、またはPolygraph上のPolymarketと統合するアプリケーション構築時に活用できます。
ethskills
Ethereum、EVM、またはブロックチェーン関連のリクエストに対応します。スマートコントラクト、dApps、ウォレット、DeFiプロトコルの構築、監査、デプロイ、インタラクションに適用されます。Solidityの開発、コントラクトアドレス、トークン規格(ERC-20、ERC-721、ERC-4626など)、Layer 2ネットワーク(Base、Arbitrum、Optimism、zkSync、Polygon)、Uniswap、Aave、Curveなどのプロトコルとの統合をカバーします。ガスコスト、コントラクトのデシマル設定、オラクルセキュリティ、リエントランシー、MEV、ブリッジング、ウォレット管理、オンチェーンデータの取得、本番環境へのデプロイ、プロトコル進化(EIPライフサイクル、フォーク追跡、今後の変更予定)といったトピックを含みます。
xxyy-trade
このスキルは、ユーザーが「トークン購入」「トークン売却」「トークンスワップ」「暗号資産取引」「取引ステータス確認」「トランザクション照会」「トークンスキャン」「フィード」「チェーン監視」「トークン照会」「トークン詳細」「トークン安全性確認」「ウォレット一覧表示」「マイウォレット」「AIスキャン」「自動スキャン」「ツイートスキャン」「オンボーディング」「IP確認」「IPホワイトリスト」「トークン発行」「自動売却」「損切り」「利益確定」「トレーリングストップ」「保有者」「トップホルダー」「KOLホルダー」などをリクエストした場合、またはSolana/ETH/BSC/BaseチェーンでXXYYを経由した取引について言及した場合に使用します。XXYY Open APIを通じてオンチェーン取引とデータ照会を実現します。